目录
引言:
那么,经过前俩天的思维练习,这几天的开题效率肯定提高了,所以我打算之后一般每天会发俩题的解题思路,然后再慢慢递增每日的题数,毕竟若一天只刷一俩题,省铜肯定是玄乎的,所以后面肯定题量会慢慢上去的
我本来今天是打算讲俩题的,但是我做完后发现第一道做的题的分数档是1300的,所以为了平衡,刚好还有一道1300的题,那就用俩道1300的题来抵一道1400的题,所以今天我们来讲三题,如图
那么,接下来,我们就开始进行题目的讲解——————>
对了,在讲题目前先提一嘴,讲的顺序是2129A,2127B然后2127C
Double Perspective
该题虽然是div1的A题,但我感觉这题比另外俩题还简单,所以我打算先讲这题,那么,我们来分析一下题目
题意分析
这是题目的链接Problem - 2129A - Codeforces
不想跳转的可看下图
这题题目虽然不长,但却令我在打的时候走歪了,我打到一半才意识到他并没有让我用最少的数对算出算大的值,只需要写选的每对数的下标就可以了,而且还可以不唯一,这说明甚至可以把无用对也选上(这个很关键,可以省很多力气),这反而还让难度下降了很多,所以我把他放在了第一题来讲、那么我们开始正式分析
首先,对于每个样例,会先输入边的个数,然后再输入每个边的起点和终点,因为是无向边,所以可以形成环,比如,1 2 ; 2 3 和 1 3就可以形成一个环,然后题目讲了f(S)和g(S)俩个数,f(S)表示的是S这个边的集合里所有的边并起来后的长度范围,g(S)表示的是S这个边的集合里能成环的边的节点数。
然后题目要求是让我们在一个大的边集合中找一个子集,使得f(S‘)-g(S‘)的值最大
那么题目意思分析完了,我们来进行逻辑的梳理
逻辑梳理
那我们想让f(S‘)-g(S‘)的值最大,肯定是要让f(S‘)尽可能大,让g(S‘)尽可能小,那么,g(S‘)我们可以让他成为0,因为若要让g(S‘)不为0,我们就肯定会有一个大线段和多个小线段来达到一个圈,如图
那么我们完全可以选择长的那一条线段,不用短的那一些线段,这样就不会形成环,f(S')也不会变小,所以通过这个思路,我们可以对所有的边进行排序,然后先按起点从小到大排,若起点一样大,就将终点从大到小排,这样排完后,只需要把每个不同的起点的第一个边选上即可,也不会形成环,得到的并线段长度也最长
接下来,我们就通过代码来实现这个思路
代码实现
思路讲完后,代码实现就很简单了,我们先看代码
首先,我们因为要输出下标,所以我们可以用结构体来存储数据,然后对结构体进行排序,带动结构体内其余变量的排序
就如我的代码中存了边的头,尾以及该边的下标
然后我们按上面逻辑梳理的那般,对结构体的排序方式进行编写,即cmp函数
然后再用vis函数来判断该点是否被访问过(不要忘了初始化)
然后就只需要用一个循环,将排序完的结构体从头到尾走一遍即可,若边的头没被访问过,就将这个头用vis标记成1,然后再将这个点的下标放入vector中存
循环结束后将vector中的数据全部输出就可以了
注意:在输出vector中的元素时,记得先输出选了几条边(这个可以在循环中解决)
那么接下来,就看AC码吧
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
#include <vector>
using namespace std;
int t;
bool vis[6010];
struct node
{
int left;
int right;
int wei;
}arr[3010];
bool cmp(node a, node b)
{
if (a.left == b.left)
return a.right > b.right;
return a.left < b.left;
}
void solve()
{
vector<int> temp;
memset(vis, 0, sizeof(vis));
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> arr[i].left >> arr[i].right;
arr[i].wei = i;
}
int ci = 0;
sort(arr + 1, arr + n + 1, cmp);
for (int i = 1; i <= n; i++)
{
if (!vis[arr[i].left])
{
vis[arr[i].left] = 1;
temp.push_back(arr[i].wei);
ci++;
}
}
cout << ci << endl;
for (int i = 0; i < temp.size(); i++)
cout << temp[i] << " ";
cout << endl;
temp.clear();
}
int main()
{
cin >> t;
while (t--)
{
solve();
}
return 0;
}
那么,这题到此就讲完啦,希望你们有所收获
接下来,我们来讲下一题,本来如果按顺序的话是应该讲2127C的,但我做下来感觉2127C的下一题比2127C简单很多(甚至可以直接暴力就能得到结果),所以我决定先将2127C的下一题,再讲2127C
Hamiiid, Haaamid... Hamid?
这题好巧不巧还是2127C那场div2的前一题,那我们来看一下这题题目
题意分析
这是题目链接Problem - 2127B - Codeforces
不想跳转可看下图
这题的题目看起来比较长,但其实看完后还是很好理解的
就是一个人负责在任何一个位置放墙,然后另一个人选择往左或者往右,然后直到撞开一道墙
一个想要堵住人不让他出去,一个想让他出去,然后问这种情况下,最快进行几次操作可以让里面的人出去,这就是题目的意思
那么,我们接下来进行逻辑的梳理
逻辑推理
·首先,想要快速走出去,肯定不可能反复折转,那么我们只需要判断俩个方向都要走几步即可
我们先算不放墙使需要走几步,然后通过循环找到往左的第一栋墙和往右的第一栋墙 ,然后假设后面全是墙,当时那个人挡上的,然后我们就可以算出刚开始动的时候,不干扰时最快走几步可以走出去
那么接下来,我们来加上开局时,加墙的干扰,我们先将玩家 初始位置前和后分别有多少空间算出来,然后就进行特判
如果前面的墙比后面的墙多,那么就将答案加上 前面墙的数量和后面的空间 的较小值
如果后面的墙比前面的墙多,那么就将答案加上 后面墙的数量和前面的空间 的较小值
如果俩边墙数量相等,就直接加上随便一边的墙的数量就可以了(因为不管开局堵哪一边,玩家只需要往另一边走就可以了)
然后有个特殊情况,就是如果玩家开局就在边界,那么只要走一次就可以出去了
那么,逻辑也就整理清楚了,是不是特别简单,接下来,我们来看代码实现
代码实现
这个其实写就很简单了,这里就直接放代码了,应该都很好懂,我看了一下没有什么需要注意的了,这里就直接放代码了
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
#include <map>
#include <vector>
using namespace std;
int t, n, x;
string a;
int main()
{
cin >> t;
while (t--)
{
cin >> n >> x;
cin >> a;
x--;
if (x == 0||x==n-1)
{
cout << "1" << endl;
continue;
}
long long ans = 1;
int hou = 0;
int qian = 0;
for (int i = x + 1; i < n; i++)
{
if (a[i] == '#')
{
hou = n - i;
break;
}
}
for (int i = x - 1; i >= 0; i--)
{
if (a[i] == '#')
{
qian = i + 1;
break;
}
}
int Hou = n - x - 1;
int Qian = x;
if (qian > hou)
{
ans += min(Hou, qian);
}
else if (qian < hou)
{
ans += min(Qian, hou);
}
else
ans += qian;
cout << ans << endl;
}
return 0;
}
那么,这题也讲完啦 ,希望对你们有所帮助
那么,接下来就进入今天的重头戏题目啦(让我栽了几次)
Trip Shopping
我们先来分析题目
题意分析
题目链接如下Problem - C - Codeforces
不想跳转的可看下图
这个题目的意思也很简单,就是有俩个人,他们都有各自的目的
一个人的目的是选择俩个下标,然后他想要最终的结果尽可能小
另一个人的目的是将上一个人选择的下标的那四个数进行位置的分配,然后他想要最终的结果尽可能大
那么其实很简单,选择权在第二个人手上,所以第一个人要做的就是不要让第二个人有调节数据的机会或者让他调节后变大的范围尽可能小
然后问操作完后,最后得到的值为多少
那么题目就分析完了,接下来,我们进行逻辑梳理部分
逻辑梳理
首先,因为他题目算哪个最后得到的值是a[i]-b[i]的绝对值,所以对换这俩个数的位置是没有问题的,那我们可以将大的数放在a数组里,小的数放在b数组里,然后再对大的数那一栏进行排序即可
那么为什么这么做呢,那么,我们通过图文结合来解释,如图,首先因为已经将大的对调上去了,也进行排序完了,i出的a比j处的a大,那么接下来,若选中了 这俩行,没有被交换时,值为下面第一个式子,若bi比bj大,那么就是第二个式子,也就是第二个人执行完交换顺序后的式子,这俩个式子相差为2(bi-aj),这便是经过操作后的最终值的增长值
那么,为了不让这个最终值增长,第一个人就一定会选择好的俩个下标,即i,j俩个下标的值已经是最大的情况了,亦或是最好的情况了,那么怎么找呢,那便是进行排序,排序肯定是按a的大小来排序,排完之后其实就只需要关注临近的就可以了(因为若bi大于aj,在已经排序完的情况下,若再往下选择,只会使得aj越来越小,使得增加的数越来越多,这是第一个人不想见到的) ,这样就可以用O(n)的时间复杂度完成这个代码
那么逻辑整理完了,接下来进入代码实现环节
代码实现
用vector来存储数据与排序,若用结构体会超时,具体可看下面用结构体时的TLE代码
要注意,vector排序时默认是从小到大,所以遍历时的条件与结构体排序完的遍历语句不一样,还有千万注意,初始时候的temp值一定要大,我一开始开了1e9都小了,所以我索性开了1e18
那么,我们先看AC代码,再来看TLE代码
AC代码如下
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
#include <vector>
using namespace std;
int t;
void solve()
{
long long ans = 0;
int n, k;
cin >> n >> k;
vector <pair<long long, long long>> arr(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> arr[i].first;
}
for (int i = 1; i <= n; i++)
{
cin >> arr[i].second;
if (arr[i].first < arr[i].second)
swap(arr[i].first, arr[i].second);
ans += arr[i].first - arr[i].second;
}
sort(arr.begin() + 1, arr.end());
long long temp = 1e18;
for (int i = 1; i < n; i++)
{
temp = min(temp, 2 * (arr[i + 1].second - arr[i].first));
}
if (temp > 0)
ans += temp;
cout << ans << endl;
}
int main()
{
cin >> t;
while (t--)
{
solve();
}
return 0;
}
TLE代码如下
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <queue>
#include <vector>
using namespace std;
int t;
struct node
{
long long a;
long long b;
}arr[20010];
bool cmp(node qian, node hou)
{
return qian.a > hou.a;
}
void solve()
{
long long ans = 0;
int n, k;
cin >> n >> k;;
for (int i = 1; i <= n; i++)
{
cin >> arr[i].a;
}
for (int i = 1; i <= n; i++)
{
cin >> arr[i].b;
if (arr[i].a < arr[i].b)
swap(arr[i].a, arr[i].b);
ans += arr[i].a - arr[i].b;
}
sort(arr + 1, arr + n + 1, cmp);
long long temp = 1e18;
for (int i = 1; i < n; i++)
{
temp = min(temp, 2 * (arr[i].b - arr[i + 1].a));
}
if (temp > 0)
ans += temp;
cout << ans << endl;
}
int main()
{
cin >> t;
while (t--)
{
solve();
}
return 0;
}
那么,今天的最后一题也讲解完啦
结语:
今日算法讲解到此结束啦,希望对你们有所帮助,谢谢观看,如果觉得不错可以分享给朋友哟