题解 P1640 【[SCOI2010]连续攻击游戏】
竟然没有
本人不会二分图,因此看到此题就想到了一个贪心解法。
yy一个大致思路:由于必须从小到大做,不难想到我们要优先保存下标较小的点。因为最终答案肯定
如何贪心呢?我们定义较小点为一对数中较小的那个,较大点为较大的那个。
由于是从小到大进行的贪心,如果当前遍历的下标有能够被选取的较大点,那么他肯定跟前面的一个未被选取的较小点相匹配,我们直接无脑选即可。
若一个点是较小点,那么就需要考虑如何选才能够使得接下来的选取能够使答案最大。一开始我的贪心思路是选择下标尽量大的对应点肯定最优。但是当下标较小的较大点数量大,而下标较大的较大点数量小的时候,这种贪心策略就可能会把下标大的较大点选完,导致答案变小。You are hacked!
但是这样的hack情况给了我们启发:我们应该选择较大点数量多的地方作为答案。因此我们可以尽量选取对应较大点 所在的位置里 较大点数量最多 的那个较小点,在数量相同的时候再进行上面的那个贪心(选择尽量大的下标),这样就可以保证最优了。(有点绕,请务必认真理解这句话)
至于具体实现,对于较大点,由于是无脑选,我们不需要知道他的对应点,直接开一个桶记录就好了;而较小点,我们可以对于每个下标开一个vector,计算的时候直接暴力算就好了。
时间复杂度
代码
#include<bits/stdc++.h>
using namespace std;
#define dd ch=getchar()
inline int read()
{
int x=0;char dd;bool f=false;
while(!isdigit(ch))f|=ch=='-',dd;
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-48,dd;
return f?-x:x;
}
#undef dd
void write(int x)
{
if(x<0)x=-x,putchar('-');
if(x>9)write(x/10);
putchar(x%10+48);
}
#define writeln(x) (write(x),putchar('\n'))
#define writesp(x) (write(x),putchar(' '))
const int N=1000005;
int n;
vector<int>xxl[N];
int cnt[N];
int main()
{
n=read();
for(int i=1,x,y;i<=n;i++)
{
x=read(),y=read();
if(x>y)swap(x,y);
cnt[y]++;
xxl[x].push_back(y);
}
int ans=n;
for(int i=1;i<=n;i++)
{
if(cnt[i])
{
cnt[i]--;
continue;
}
if(!xxl[i].size()){ans=i-1;break;}
int res=xxl[i][0];
for(int j=1;j<(int)xxl[i].size();j++)
if(cnt[xxl[i][j]]>cnt[res]||(cnt[xxl[i][j]]==cnt[res]&&xxl[i][j]>res))
res=xxl[i][j];
cnt[res]--;
}
writeln(ans);
return 0;
}