1. 程式人生 > >1018 - 網路流最大匹配&神建圖 - 卡牌配對(BZOJ 4205)

1018 - 網路流最大匹配&神建圖 - 卡牌配對(BZOJ 4205)

卡牌配對

「問題描述」

現在有一種卡牌遊戲,每張卡牌上有三個屬性值:A,B,C。把卡牌分為X,Y兩類,分別有n1,n2張。

兩張卡牌能夠配對,當且僅當,存在至多一項屬性值使得兩張卡牌該項屬性值互質,且兩張卡牌類別不同。

比如一張X類卡牌屬性值分別是225,233,101,一張Y類卡牌屬性值分別為115,466,99。那麼這兩張牌是可以配對的,因為只有101和99一組屬性互質。

遊戲的目的是最大化匹配上的卡牌組數,當然每張卡牌只能用一次。

「輸入」

資料第一行兩個數n1,n2,空格分割。

接下來n1行,每行3個數,依次表示每張X類卡牌的3項屬性值。

接下來n2行,每行3個數,依次表示每張Y類卡牌的3項屬性值。

「輸出」

輸出一個整數:最多能夠匹配的數目。

「樣例輸入」

2 2

2 2 2

2 5 5

2 2 5

5 5 5

「樣例輸出」

2

「提示」

樣例中第一張X類卡牌和第一張Y類卡牌能配對,第二張X類卡牌和兩張Y類卡牌都能配對。所以最佳方案是第一張X和第一張Y配對,第二張X和第二張Y配對。

另外,請大膽使用漸進複雜度較高的演算法!

「資料規模與約定」

對於10%的資料,n1,n2≤ 10;

對於50%的資料,n1,n2≤ 3000。

對於100%的資料,n1,n2≤ 30000,屬性值為不超過200的正整數

 

分析

這個建圖可以有

首先n^2建圖,當然是不可過的

那怎麼辦呢?我們思考題目中說“至多有一項屬性值互質”--->“至少有兩項不互質”-->至少有兩項屬性值分別含有相同的質因數

由於質因數的大小<200,個數也就只有46個,是個突破口

 

考慮屬性值(a,b,c)總共會出現三種情況:ab,ac,bc(ab表示X類中的a屬性,b屬性和Y類中的a屬性,b屬性不互質)

我們在中間建一排點,對於第一類ab(其餘類以此類推),考慮 A 項屬性值能被 x 整除且 B 項屬性值能被 y 整除的所有點(x,y),只要是在這個點連線的兩側一定能夠匹配,所以我們在匹配的網路流模型中間增加一排這樣的點,滿足要求的左右點分別與它相連,邊權為正無窮

 

聽說匈牙利只能過10分???哈,還是網路流優秀

牢騷

本來是打算自己好好琢磨琢磨,結果巨神zzh跑過來說這道題建圖很妙……一聽,我就覺得自己不可做了,然後就……看題解了

但大體思路方向應該還是對了一些的,都是從其屬性值下手(<=200)

 

程式碼

#include<bits/stdc++.h>
#define P 205
#define in read()
#define N 80009//pay attention to the 範圍……
#define M 4000000
#define inf (1ll<<31)-1
using namespace std;
inline int read(){
	char ch;int f=1,res=0;
	while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	return f==1?res:-res;
}
char xxx;
bool mark[P];
int pri[P],num=0;
int n,m,S,T;
struct node{
	int x,y,z;
}a[N],b[N];
vector<int > g[205];
int nxt[M],head[N],to[M],cap[M],id[205][205];
int lev[N],cur[N];
char yyy;
inline void init(){
	int i,j;
	mark[1]=1;
	for(i=2;i<=200;++i){
		if(!mark[i]) pri[++num]=i;
		for(j=1;j<=num&&pri[j]*i<=200;++j){
			mark[pri[j]*i]=1;
			if(i%pri[j]==0) break;
		}
	}
	for(i=2;i<=200;++i){
		for(j=1;j<=num;++j)
			if(!(i%pri[j])) g[i].push_back(j);
	}
	int tot=0;
	for(i=1;i<=num;++i)
		for(j=1;j<=num;++j)
			id[i][j]=++tot;
}
int cnt=1;
inline void add(int x,int y,int z){
	nxt[++cnt]=head[x];head[x]=cnt;to[cnt]=y;cap[cnt]=z;
	nxt[++cnt]=head[y];head[y]=cnt;to[cnt]=x;cap[cnt]=0;
}
inline void link(int pos,int type){
	int x,y,z;
	if(!type) x=a[pos].x,y=a[pos].y,z=a[pos].z;
	else x=b[pos].x,y=b[pos].y,z=b[pos].z;
	int i,j,k;
	for(i=0;i<g[x].size();++i)
		for(j=0;j<g[y].size();++j)
		{
			if(!type) add(pos,id[g[x][i]][g[y][j]]+n+m,1);
			else add(id[g[x][i]][g[y][j]]+n+m,pos+n,1);
		}	
	for(i=0;i<g[x].size();++i)
		for(j=0;j<g[z].size();++j)
		{
			if(!type) add(pos,id[g[x][i]][g[z][j]]+n+m+46*46,1);
			else add(id[g[x][i]][g[z][j]]+n+m+46*46,pos+n,1);
		}	
	for(i=0;i<g[y].size();++i)
		for(j=0;j<g[z].size();++j)
		{
			if(!type) add(pos,id[g[y][i]][g[z][j]]+n+m+46*46*2,1);
			else add(id[g[y][i]][g[z][j]]+n+m+46*46*2,pos+n,1);
		}	
}
inline bool bfs(){
	for(int i=S;i<=T;++i){
		lev[i]=-1;cur[i]=head[i];
	}
	queue<int > q;
	q.push(S);lev[S]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int e=head[u];e;e=nxt[e]){
			int v=to[e];
			if(cap[e]<=0||lev[v]!=-1) continue;
			lev[v]=lev[u]+1;
			if(v==T) return true;
			q.push(v);
		}
	}
	return false;
}
inline int dinic(int u,int flow){
	if(u==T) return flow;
	int delta,res=0;
	for(int &e=cur[u];e;e=nxt[e]){
		int v=to[e];
		if(lev[v]>lev[u]&&cap[e]){
			delta=dinic(v,min(flow-res,cap[e]));
			if(delta){
				cap[e]-=delta;cap[e^1]+=delta;
				res+=delta;if(res==flow) return res;
			}
		}
	}
	return res;
}
int main(){
	init();
	n=in;m=in;
	S=0;T=n+m+46*46*3+1;
	int i,j,k;
	for(i=1;i<=n;++i) a[i].x=in,a[i].y=in,a[i].z=in;
	for(i=1;i<=m;++i) b[i].x=in,b[i].y=in,b[i].z=in;
	for(i=1;i<=n;++i) add(S,i,1),link(i,0);
	for(i=1;i<=m;++i) add(i+n,T,1),link(i,1);
	int maxflow=0;
	while(bfs()) maxflow+=dinic(S,inf);
	printf("%d",maxflow);
	return 0;
}