WC2016 挑战NPC

NPC,即Non_Player Character,作为游戏很重要的一种存在……

哎不对,扯远了。

这题出题人卖萌,明显不是NPC问题。

我们可以发现(通过前几个点找一找规律什么的)这题可以建立一个一般图最大匹配模型。

首先将所有的筐子拆成3个点,任选其中两点连边,然后对于每一个条件,将对应球与筐子的三个点分别连边。

可以证明(不会),最大匹配中所有球一定是匹配了的。

于是就可以用带花树(没学过)求解一般图最大匹配了。

复杂度O(VE),和二分图最大匹配一样

代码写挫了,跑了78MS。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=600+5;
struct Edge{int to,next;}e[N*N];
int head[N],cnt;
void ins(int u,int v){
	e[++cnt]=(Edge){v,head[u]};head[u]=cnt;
}
void insert(int u,int v){
	ins(u,v);ins(v,u);
}
int pa[N];
int find(int x){
	return pa[x]==x?x:pa[x]=find(pa[x]);
}
void merge(int u,int v){
	u=find(u);v=find(v);
	if(u!=v)pa[u]=v;
}
int n,linked[N],next[N],que[N],front,rear,mark[N],vis[N];
int lca(int u,int v){
	static int T=0;T++;
	while(true){
		while(u!=-1){
			u=find(u);
			if(vis[u]==T)return u;
			vis[u]=T;
			if(linked[u]!=-1)u=next[linked[u]];
			else u=-1;
		}
		swap(u,v);
	}
}
void blossom(int a,int t){
	while(a!=t){
		int b=linked[a],c=next[b];
		if(find(c)!=t)next[c]=b;
		if(mark[b]==2)mark[que[++rear]=b]=1;
		if(mark[c]==2)mark[que[++rear]=c]=1;
		merge(a,b);merge(b,c);
		a=c;
	}
}
void aug(int s){
	for(int i=1;i<=n;i++)
	next[i]=vis[i]=-1,mark[i]=0,pa[i]=i;
	mark[s]=1;que[rear=0]=s;
	for(front=0;linked[s]==-1&&front<=rear;front++){
		int u=que[front];
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;
			if(linked[u]==v||find(u)==find(v)||mark[v]==2)continue;
			if(mark[v]==1){
				int w=lca(u,v);
				if(find(u)!=w)next[u]=v;
				if(find(v)!=w)next[v]=u;
				blossom(u,w);
				blossom(v,w);
			}else if(linked[v]==-1){
				next[v]=u;
				for(u=v;u!=-1;){
					v=next[u];
					int tmp=linked[v];
					linked[u]=v;linked[v]=u;
					u=tmp;
				}
				break;
			}else{
				next[v]=u;
				mark[que[++rear]=linked[v]]=1;
				mark[v]=2;
			}
		}
	}
}
int sz,node[105][3],belong[N];
int main(){
	int cas;scanf("%d",&cas);
	while(cas--){
		 int a,b,c;scanf("%d%d%d",&a,&b,&c);
		 memset(head,0,sizeof(head));cnt=0;
		 sz=a;
		 for(int i=1;i<=b;i++){
		 	node[i][0]=++sz;belong[sz]=i;
			node[i][1]=++sz;belong[sz]=i;
			node[i][2]=++sz;belong[sz]=i;
			insert(node[i][0],node[i][1]);
		}
		n=sz;
		for(int i=1;i<=c;i++){
			int u,v;scanf("%d%d",&u,&v);
			for(int j=0;j<3;j++)
			insert(u,node[v][j]);
		}
		memset(linked,-1,sizeof(linked));
		for(int i=1;i<=n;i++)
		if(linked[i]==-1)aug(i);
		int ans=0;
		for(int i=1;i<=n;i++)if(linked[i]!=-1)ans++;
		printf("%d\n",ans/2-a);
		for(int i=1;i<=a;i++)
		printf("%d ",belong[linked[i]]);
		putchar('\n');
	}
	return 0;
}
		


才过了一个月发现带花树这东西感觉跟没学过一样啊TAT,蒟蒻表示有点方。

然而这题总不能就这么不会了吧,于是从论文里学了一种特殊的玩泥巴技巧

大概就是随机化+匈牙利,最好战绩水到了80分QAQ,果然数据是特殊构造的。。。。

有两个点T了,感觉时间宽一点大概就A了吧。

顺便说这个代码A掉了模板题库里的一般图最大匹配,这时候应该有老司机来hack我。。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<ctime>
using namespace std;
const int N=600+5;
bool g[N][N];
bool vis[N];
int linked[N],p[N],q[N],n,anslink[N],ans;
int ba,bl;
void ins(int u,int v){
	g[u][v]=g[v][u]=1;
}
bool match(int u){
	vis[u]=1;
	for(int i=1;i<=n;i++){
		int v=q[i];if(vis[v]||!g[u][v])continue;
		vis[v]=1;
		if(!linked[v]||match(linked[v])){
			linked[v]=u;
			linked[u]=v;
			return true;
		}
	}
	return false;
}
void update(int tmp){
	ans=tmp;
	for(int i=1;i<=ba;i++)
	anslink[i]=linked[i];
}
void work(){
	memset(linked,0,sizeof(linked));
	int tmp=0;
	for(int i=1;i<=n;i++)
	if(!linked[p[i]])
	for(int t=1;t<=10;t++){
		memset(vis,0,sizeof(vis));
		if(match(p[i])){
			tmp++;break;
		}else{
			for(int j=1;j<=n;j++){
				int k=j+rand()%(n-j+1);
				swap(q[j],q[k]);
			}
		}
	}
	if(tmp>ans)update(tmp);
}
int b1(int i){
	return ba+i;
}
int b2(int i){
	return ba+bl+i;
}
int b3(int i){
	return ba+bl+bl+i;
}
int calc(int x){
	if(x<=ba+bl)return x-ba;
	if(x<=ba+bl+bl)return x-ba-bl;
	return x-ba-bl-bl;
}
int main(){
	//freopen("a.in","r",stdin);
	int T;scanf("%d",&T);
	srand(541213);
	while(T--){
		int e;
		scanf("%d%d%d",&ba,&bl,&e);
		memset(g,0,sizeof(g));
		for(int i=1;i<=bl;i++)
		ins(b1(i),b2(i));
		n=ba+bl*3;
		int a,b;
		while(e--){
			scanf("%d%d",&a,&b);
			ins(a,b1(b));
			ins(a,b2(b));
			ins(a,b3(b));
		}
		ans=0;
		for(int i=1;i<=n;i++)p[i]=q[i]=i;
		for(int k=1;k<=10;k++){
			for(int i=1;i<=ba;i++){
				int j=i+rand()%(ba-i+1);
				swap(p[i],p[j]);
			}
			for(int i=1;i<=n;i++){
				int j=i+rand()%(n-i+1);
				swap(q[i],q[j]);
			}
			work();
		}
		printf("%d\n",ans-ba);
		for(int i=1;i<=ba;i++)
		printf("%d ",calc(anslink[i]));
		putchar('\n');
	}
	return 0;
}


你可能感兴趣的:(WC2016 挑战NPC)