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;
}