BZOJ4205卡牌配对——最大流+建图优化
生活随笔
收集整理的這篇文章主要介紹了
BZOJ4205卡牌配对——最大流+建图优化
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目描述
現在有一種卡牌游戲,每張卡牌上有三個屬性值: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 22 2 2
2 5 5
2 2 5
5 5 5
樣例輸出
2【提示】
樣例中第一張X類卡牌和第一張Y類卡牌能配對,第二張X類卡牌和兩張Y類卡牌都能配對。所以最佳方案是第一張X和第一張Y配對,第二張X和第二張Y配對。
另外,請大膽使用漸進復雜度較高的算法!
提示
對于100%的數據,n1,n2≤ 30000,屬性值為不超過200的正整數?乍一看就是一個簡單的二分圖最大匹配,枚舉任意兩個不同類的卡牌連邊即可,但光是枚舉的時間復雜度就爆炸了,何況還會跑$10^9$條邊的最大流。所以我們想辦法優化:先考慮兩張牌$A,B$屬性都分別不互質的情況(剩下兩種情況相同),我們找到$A$屬性是$x$的倍數、$B$屬性是$y$的倍數的所有點,在二分圖中間新建一個點然后將左右滿足要求的點都連向這個點,流量為$INF$,表示這些點之間都能互相匹配。這里的$x,y$顯然只需要枚舉質數即可,$200$之內的質數只有$46$個,所以對于一種情況只需要建$46*46$個點即可,總點數為$70000$。而$2*3*5*7>200$,所以每個數最多只有三個質因子,每個點最多連$3*3*3$條邊,總邊數為$2000000$,直接跑$dinic$即可。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> #define INF 0x3f3f3f3f #define ll long long using namespace std; int head[70000]; int to[4000000]; int next[4000000]; int val[4000000]; int d[70000]; int q[70000]; int back[70000]; int S,T; int n,m; int tot=1; int ans; int cnt; vector<int>v[210]; int s[210][210]; int a1[40000]; int b1[40000]; int c1[40000]; int a2[40000]; int b2[40000]; int c2[40000]; int prime[50]; int vis[210]; int num; void find() {for(int i=2;i<=200;i++){if(!vis[i]){prime[++cnt]=i;}for(int j=1;j<=cnt&&prime[j]*i<=200;j++){vis[i*prime[j]]=1;if(i%prime[j]==0){break;}}}for(int i=1;i<=200;i++){for(int j=1;j<=cnt;j++){if(i%prime[j]==0){v[i].push_back(j);}}}for(int i=1;i<=cnt;i++){for(int j=1;j<=cnt;j++){s[i][j]=++num;}} } void add(int x,int y,int v) {tot++;next[tot]=back[x];back[x]=tot;to[tot]=y;val[tot]=v;tot++;next[tot]=back[y];back[y]=tot;to[tot]=x;val[tot]=0; } bool bfs(int S,int T) {int r=0;int l=0;memset(d,-1,sizeof(d));q[r++]=T;d[T]=2;while(l<r){int now=q[l];for(int i=back[now];i;i=next[i]){if(d[to[i]]==-1&&val[i^1]!=0){d[to[i]]=d[now]+1;q[r++]=to[i];}}l++;}if(d[S]==-1){return false;}else{return true;} } int dfs(int x,int flow) {if(x==T){return flow;}int now_flow;int used=0;for(int &i=head[x];i;i=next[i]){if(d[to[i]]==d[x]-1&&val[i]!=0){now_flow=dfs(to[i],min(flow-used,val[i]));val[i]-=now_flow;val[i^1]+=now_flow;used+=now_flow;if(now_flow==flow){return flow;}}}if(used==0){d[x]=-1;}return used; } int dinic() {int res=0;while(bfs(S,T)){memcpy(head,back,sizeof(back));res+=dfs(S,0x3f3f3f3f);}return res; } int main() {find();scanf("%d%d",&n,&m);S=n+m+46*46*3+1;T=S+1;for(int i=1;i<=n;i++){scanf("%d%d%d",&a1[i],&b1[i],&c1[i]);}for(int i=1;i<=m;i++){scanf("%d%d%d",&a2[i],&b2[i],&c2[i]);}for(int i=1;i<=n;i++){add(S,i,1);int sz1=v[a1[i]].size();int sz2=v[b1[i]].size();for(int j=0;j<sz1;j++){for(int k=0;k<sz2;k++){add(i,n+m+s[v[a1[i]][j]][v[b1[i]][k]],INF);}}sz2=v[c1[i]].size();for(int j=0;j<sz1;j++){for(int k=0;k<sz2;k++){add(i,n+m+46*46+s[v[a1[i]][j]][v[c1[i]][k]],INF);}}sz1=v[b1[i]].size();for(int j=0;j<sz1;j++){for(int k=0;k<sz2;k++){add(i,n+m+2*46*46+s[v[b1[i]][j]][v[c1[i]][k]],INF);}}}for(int i=1;i<=m;i++){add(n+i,T,1);int sz1=v[a2[i]].size();int sz2=v[b2[i]].size();for(int j=0;j<sz1;j++){for(int k=0;k<sz2;k++){add(n+m+s[v[a2[i]][j]][v[b2[i]][k]],n+i,INF);}}sz2=v[c2[i]].size();for(int j=0;j<sz1;j++){for(int k=0;k<sz2;k++){add(n+m+46*46+s[v[a2[i]][j]][v[c2[i]][k]],n+i,INF);}}sz1=v[b2[i]].size();for(int j=0;j<sz1;j++){for(int k=0;k<sz2;k++){add(n+m+2*46*46+s[v[b2[i]][j]][v[c2[i]][k]],n+i,INF);}}}printf("%d",dinic()); }轉載于:https://www.cnblogs.com/Khada-Jhin/p/10584701.html
總結
以上是生活随笔為你收集整理的BZOJ4205卡牌配对——最大流+建图优化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: windows 7系统搭建本地SVN服务
- 下一篇: Titanium 列表显示TableVi