网络流合集:bzoj1433,1934,1854 题解
转载请注明:http://blog.csdn.net/jiangshibiao/article/details/23992205
网络流/二分图大合集
【NO.1*原题】
1433: [ZJOI2009]假期的宿舍
Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 972 Solved: 422
[Submit][Status]
Description
Input
Output
Sample Input
3
1 1 0
0 1 0
0 1 1
1 0 0
1 0 0
Sample Output
HINT
对于30% 的数据满足1 ≤ n ≤ 12。
对于100% 的数据满足1 ≤ n ≤ 50,1 ≤ T ≤ 20。
【NO.1*分析】想了非常长时间。igzhou大神非要我用二分图写,但我就是要转化成网络流。首先我们把住校的人(也就是有床的人)的床和汇点T连边。注意这是”床“。而不是人本身。
然后把源点S和全部人连一条边。
再把互相认识的人中人和床连边。
比方1是住校的,2是走读的。2认识1,就把2和1的床连边。
注意不是2和1连边。
自然,对于住校生。自己和自己的床也要连边。
以上全部边的容量都是1。
坑点:输出不用回车。
【NO.1*代码】
#include<cstdio> #include<algorithm> #include<cstring> #define N 305 #define INF 2000000000 using namespace std; int map[N][N],f[N],q[N],num[N],x,test,ans,n,m,i,j,cnt; bool bfs() { memset(f,-1,sizeof(f)); int h=0,t=1;q[1]=0;f[0]=1; while (h<t) { int now=q[++h];if (now==n) return 1; for (int i=0;i<=n;i++) if (map[now][i]&&f[i]==-1) { f[i]=f[now]+1;q[++t]=i; } } return 0; } int dinic(int sta,int sum) { if (sta==n) return sum; int os=sum; for (int i=0;(i<=n)&&os;i++) if (map[sta][i]&&f[i]==f[sta]+1) { int Min=dinic(i,min(map[sta][i],os)); map[sta][i]-=Min;map[i][sta]+=Min;os-=Min; } if (os==sum) f[sta]=-1; return sum-os; } int main() { scanf("%d",&test); while (test) { test--;memset(map,0,sizeof(map)); scanf("%d",&n);cnt=0; for (i=1;i<=n;i++) { scanf("%d",&num[i]); if (num[i]) map[i+n][2*n+1]=1; } for (i=1;i<=n;i++) { scanf("%d",&x); if (!num[i]||num[i]&&!x) map[0][i]=1,cnt++; } for (i=1;i<=n;i++) for (j=1;j<=n;j++) { scanf("%d",&x); if (x||i==j) map[i][j+n]=1; } n=2*n+1;ans=0; while (bfs()) ans+=dinic(0,INF); if (ans==cnt) puts("^_^");else puts("T_T"); } return 0; }
【NO.2*原题】
1934: [Shoi2007]Vote 善意的投票
Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 897 Solved: 537
[Submit][Status]
Description
我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和全部和自己本来意愿发生冲突的人数。 我们的问题就是,每位小朋友应该如何投票,才干使冲突数最小?
Input
接下来文件还有m行,每行有两个整数i,j。
表示i,j是一对好朋友,我们保证不论什么两对i,j不会反复。
Output
Sample Input
1 0 0
1 2
1 3
3 2
Sample Output
HINT
在第一个样例中,全部小朋友都投赞成票就能得到最优解
Source
【NO.2*分析】我感觉这个的建图有点诡异。把全部1的小朋友和S连,否则和T连。再朋友间互相连。求最大流。
【NO.2*代码】
#include<cstdio> #include<algorithm> #include<cstring> #define N 302 #define INF 2000000000 using namespace std; int map[N][N],f[N],q[N],num,x,y,ans,n,m,i; bool bfs() { memset(f,-1,sizeof(f)); int h=0,t=1;q[1]=0;f[0]=1; while (h<t) { int now=q[++h];if (now==n) return 1; for (int i=0;i<=n;i++) if (map[now][i]&&f[i]==-1) { f[i]=f[now]+1; q[++t]=i; } } return 0; } int dinic(int sta,int sum) { if (sta==n) return sum;int os=sum; for (int i=0;(i<=n)&&os;i++) if (map[sta][i]&&f[i]==f[sta]+1) { int Min=dinic(i,min(map[sta][i],os)); map[sta][i]-=Min;map[i][sta]+=Min;os-=Min; } if (os==sum) f[sta]=-1;return sum-os; } int main() { scanf("%d%d",&n,&m); for (i=1;i<=n;i++) { scanf("%d",&num);if (num) map[0][i]=1;else map[i][n+1]=1; } for (i=1;i<=m;i++) { scanf("%d%d",&x,&y);map[x][y]=map[y][x]=1; } n++;while (bfs()) ans+=dinic(0,INF); printf("%d",ans); return 0; }
【NO.3*原题】
1854: [Scoi2010]游戏
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 1954 Solved: 689
[Submit][Status]
Description
也就是说一開始的时候,lxhgww仅仅能使用某个属性值为1的装备攻击boss,然后仅仅能使用某个属性值为2的装备攻击boss,然后仅仅能使用某个属性值为3的装备攻击boss……以此类推。 如今lxhgww想知道他最多能连续攻击boss多少次?
Input
Output
Sample Input
1 2
3 2
4 5
Sample Output
HINT
【数据范围】
对于30%的数据。保证N < =1000
对于100%的数据,保证N < =1000000
Source
【NO.3*分析】这个构图是挺经典的。最后我是用二分图A的。
事实上这也挺好理解。左側是各种属性,右側是各种装备。对于每个装备,连两个属性。
这样就能保证选了一个后,对于同一个装备的还有一个属性,就不会选了。
【NO.3*代码】
#include<cstdio> #include<cstring> using namespace std; struct arr{int go,next;}a[2000005]; int belong[1000005],end[10005]; int visit[1000005]; int x,y,n,i,cnt; char ch; void add(int x,int y) { a[++cnt].go=y;a[cnt].next=end[x];end[x]=cnt; } inline int Read() { while (ch<'0'||ch>'9') ch=getchar(); int s=0;while (ch>='0'&&ch<='9') s=s*10+ch-48,ch=getchar();return s; } bool find(int k) { for (int i=end[k];i;i=a[i].next) { int go=a[i].go; if (visit[go]==i) continue; visit[go]=i; if (!belong[go]||find(belong[go])) { belong[go]=k; return true; } } return false; } int main() { scanf("%d",&n);ch=' '; for (i=1;i<=n;i++) { x=Read();y=Read(); add(x,i);add(y,i); } memset(belong,0,sizeof(belong)); for (i=1;i<=10000;i++) if (!find(i)) break; i--;printf("%d",i);/ return 0; }