二分圖刷題計劃
阿新 • • 發佈:2018-11-03
最近忽然想刷二分圖
在賽前去洛谷做了做二分圖的題目
[SCOI2010]連續攻擊遊戲
我們很容易發現每選用一個物品,只能採取一個屬性值,那麼另一個屬性值就無法使用了,我們無法確定應該用哪個屬性值,這就比較難找到最優的選擇。
我們就可以考慮將一個屬性值為\(x,y\)的物品\(i\)處理一下,將\(x->i,y->i\)連邊,然後就可以保證任意一個物品都只選擇了一個屬性,建出二分圖跑一遍最大匹配即可得到最優解,由於屬性連續,所以找到第一個不可匹配的就要退出。
這題資料範圍是真的fake,據說最大的資料才\(7e4\),資料範圍寫的\(1e6\)好多暴力都跑過去了,按理說匈牙利也不可能過的。
程式碼(匈牙利板子):
#include<cstdio> #include<cstring> using namespace std; int n,cnt,pre[2000001],nxt[2000001],h[1000001],vis[2000001];bool used[2000001]; void add(int x,int y){pre[++cnt]=y;nxt[cnt]=h[x];h[x]=cnt;} bool dfs(int x) { for(int i=h[x];i;i=nxt[i]) if(!used[pre[i]]) { used[pre[i]]=1; if(!vis[pre[i]]||dfs(vis[pre[i]])) { vis[pre[i]]=x; return 1; } } return 0; } int main() { scanf("%d",&n); for(int i=1,x,y;i<=n;i++)scanf("%d%d",&x,&y),add(x,i+n),add(y,i+n); for(int i=1;i<=n;i++){memset(used,0,sizeof used);if(!dfs(i)){printf("%d\n",i-1);return 0;}} printf("%d\n",n); }
[ZJOI2007]矩陣遊戲
這道題我一開始想的就是隻要每行每列都有一個\(1\)就可以了,這是不是很愚蠢。。。
隨手hack:
顯然是\(No\),但是如果按我之前的想法就錯了。
但是思想還是對的,只要每行和每列匹配上就行了。
但是交換會影響多個點。
我們暫時只考慮行匹配完全,
於是我們考慮對於每個黑點,他的位置\((i,j)\),就意味著他能通過列交換使第\(i\)行匹配上。
所以我們只要將對於所有黑點連邊,最後求一遍最大匹配,判斷一下最大匹配是否是\(n\)就行了。
顯然是\(n\)即\(Yes\),否則\(No\)
程式碼;
#include<cstdio> #include<cstring> using namespace std; int t,n,mp[201][201],f[201];bool vis[201],flag; bool dfs(int x){for(int i=1;i<=n;i++)if(!vis[i]&&mp[x][i]){vis[i]=1;if(!f[i]||dfs(f[i])){f[i]=x;return 1;}}return 0;} int main() { scanf("%d",&t); while(t--) { memset(f,0,sizeof f); scanf("%d",&n);flag=0; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&mp[i][j]); for(int i=1;i<=n;i++){memset(vis,0,sizeof vis);if(!dfs(i)){printf("No\n");flag=1;break;}} if(!flag)printf("Yes\n"); } }