1305. [CQOI2009]跳舞【最大流+二分】
阿新 • • 發佈:2018-03-31
else edge fine string 男女 nbsp CP ios clu
YYY
YYY
YYY
Description
一次舞會有n個男孩和n個女孩。每首曲子開始時,所有男孩和女孩恰好配成n對跳交誼舞。每個男孩都不會和同一個女孩跳兩首(或更多)舞曲。有一些男孩女孩相互喜歡,而其他相互不喜歡(不會“單向喜歡”)。每個男孩最多只願意和k個不喜歡的女孩跳舞,而每個女孩也最多只願意和k個不喜歡的男孩跳舞。給出每對男孩女孩是否相互喜歡的信息,舞會最多能有幾首舞曲?
Input
第一行包含兩個整數n和k。以下n行每行包含n個字符,其中第i行第j個字符為‘Y‘當且僅當男孩i和女孩j相互喜歡。
Output
僅一個數,即舞曲數目的最大值。
Sample Input
3 0YYY
YYY
YYY
Sample Output
3HINT
N<=50 K<=30
這個題思路很妙啊……第一次做到在網絡流中使用二分的題目
其實一開始想到用二分限制的,不過並沒有深入思考下去
而是寫了一個別人幾行就能實現我卻用網絡流實現的貪心
正解是拆點加二分。
因為答案滿足單調性,若可以跳x首曲子,則x-1首肯定也是可以的
建圖?
將每個男生和女生拆成兩個點:Yes和No
超級源點連接男生的Yes,超級匯點連接女生的Yes
容量設置為二分的x。若跑出來最大流是x*n,就說明滿流了
滿流即為可以滿足x首曲子
對於互相喜歡的,我們在男女的兩個Yes點間連邊
不喜歡的,我們就在男女的兩個No點間連邊
那麽如何限制k呢?
每個男Yes連自己的No,容量為k
每個女No連自己Yes,容量為k
這樣就可以保證k了
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #define MAXM (100000+10) #define MAXN (10000+10) using namespace std; struct node { int Flow; int next; int to; }edge[MAXM*2]; int Depth[MAXN],q[MAXN]; int head[MAXN],num_edge; int n,m,k,s,e,d,INF; int a[MAXN][MAXN]; char ch[1010]; void add(int u,int v,int l) { edge[++num_edge].to=v; edge[num_edge].Flow=l; edge[num_edge].next=head[u]; head[u]=num_edge; } bool Bfs(int s,int e) { int Head=0,Tail=1; memset(Depth,0,sizeof(Depth)); Depth[s]=1; q[1]=s; while (Head<Tail) { ++Head; int x=q[Head]; for (int i=head[x];i!=0;i=edge[i].next) if (!Depth[edge[i].to] && edge[i].Flow>0) { Depth[edge[i].to]=Depth[x]+1; q[++Tail]=edge[i].to; } } if (Depth[e]>0) return true; return false; } int Dfs(int x,int low) { int Min,f=0; if (x==e || low==0) return low; for (int i=head[x];i!=0;i=edge[i].next) if (edge[i].Flow>0 && Depth[edge[i].to]==Depth[x]+1 && (Min=Dfs(edge[i].to , min(low,edge[i].Flow) ))) { edge[i].Flow-=Min; edge[((i-1)^1)+1].Flow+=Min; f+=Min; low-=Min; } return f; } int Dinic(int s,int e) { int Ans=0; while (Bfs(s,e)) Ans+=Dfs(s,0x7fffffff); return Ans; } void Addline(int x) { memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); num_edge=0; for (int i=1;i<=n;++i) { add(0,i+520,x); add(i+520,0,0);//超級源點 add(i+n+520,999,x);//超級匯點 add(999,i+n+520,0); add(i+520,i+250,k); add(i+250,i+520,0); add(i+n+250,i+n+520,k); add(i+n+520,i+n+250,0); } for (int i=1;i<=n;++i) { for (int j=1;j<=n;++j) if (a[i][j]==1) { add(i+520,j+n+520,1); add(j+n+520,i+520,0); } else { add(i+250,j+n+250,1); add(j+n+250,i+250,0); } } } int main() { s=0,e=999; memset(&INF,0x7f,sizeof(INF)); scanf("%d%d",&n,&k); for (int i=1;i<=n;++i) { scanf("%s",ch); for (int j=1;j<=n;++j) a[i][j]=(ch[j-1]==‘Y‘); } int l=0,r=n*n; while (l<r) { int mid=(l+r+1)/2; Addline(mid); int Max=Dinic(0,999); if (Max==mid*n) l=mid; else r=mid-1; } cout<<l<<endl; }
1305. [CQOI2009]跳舞【最大流+二分】