题意:n个物品,每个物品两个权值,要求拼凑成一个1….m的序列,每个物品只能用一次,每个物品只能选择其中一个权值,问m最大是多少,n<=1e6.
这题我一开始第一反应其实就是二分图匹配之类的东西,但是没有细想觉得DP好像更靠谱(个鬼),然后dp了半天d不出来,觉得是不是什么奇怪的贪心,结果还是WA,最后看题解才发现就是二分图匹配,不过还有并查集做法= =
并查集的话就有一个性质,每个物品的两个属性a,b之间连边,那么一个连通块内如果是一个树,那么其中的n-1个节点都是可以满足的,如果是个环,那么n个点都可以满足,那么根据这个性质来并查集。
用并查集维护图的连通性,对于每个集合记录这个联通块有没有环,以及这个联通块中最大的点是多少,如果某个点在并查集内的联通块无环而且这个点在联通块内最大那么就是最大的m了。
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+5;
int n,m;
int sz[N],f[N],vis[N];
inline int find(int x)
{
if (f[x]==x)return x;
else return f[x]=find(f[x]);
}
inline void un(int x,int y)
{
if (x>y)swap(x,y);
f[x]=y;
vis[x]=1;
}
int main()
{
scanf("%d",&n);
fo(i,1,n+1)f[i]=i;
fo(i,1,n)
{
int x,y;
scanf("%d%d",&x,&y);
int fx=find(x),fy=find(y);
if (fx==fy)vis[fx]=1;
else un(fx,fy);
}
fo(i,1,n+1)if (!vis[i])
{
printf("%d\n",i-1);
return 0;
}
}
好像二分图匹配更显然?
%%%po姐。既然权值不超过1e4那么暴力枚举权值看看每种权值是否能匹配成功。
代码就直接copy过来了,有需要请移步
#include
#include
#include
#include
#define M 1001001
using namespace std;
struct abcd{
int to,next;
}table[M<<1];
int head[M],tot;
int n,m;
int result[M],state[M],T;
void Add(int x,int y)
{
table[++tot].to=y;
table[tot].next=head[x];
head[x]=tot;
}
bool Hungary(int x)
{
int i;
for(i=head[x];i;i=table[i].next)
{
if(state[table[i].to]==T)
continue;
state[table[i].to]=T;
if( !result[table[i].to] || Hungary(result[table[i].to]) )
{
result[table[i].to]=x;
return true;
}
}
return false;
}
int main()
{
int i,x,y;
cin>>m;
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
Add(x,i);Add(y,i);
}
for(i=1;i<=10000;i++)
{
++T;
if( !Hungary(i) )
break;
}
cout<1<