poj 2396 有下界的最大流(填充矩阵)

来源:互联网 发布:招聘游戏程序员 编辑:程序博客网 时间:2024/05/29 19:30

题意:给定若干限制条件填充n*m非负矩阵。约束条件包括:1、 每行数字之和;2、 每列数字之和;3、某些格子里需填数据的具体值或上下界。(输入中的0代表整行或者整列)。如果存在满足所有约束的条件的矩阵,则输出该方案。否则输出IMPOSSIBLE.

思路(北京大学郭炜老师课件):可转化成容量有上下界的最大流(需要先添加虚拟源点和虚拟汇点来判断可行流)问题:将方阵的行从1……n编号,列n+1……n+m编号,添加源点s=0和汇点t=n+m+1.     

1>将源点和每一个行节点相连,相连所形成的边的容量和下界置为该行所有数字的和             

2>将每一个列节点和汇点相连,相连所形成的边的容量和下界都置为该列所有数字的和

3>从每个行节点到每个列节点连边,容量为无穷大

4> 如果u行v列的数字必须大于w,则以w+1更新边<u,v+n>的容量下界

5> 如果u行v列的数字必须小于w,则以w-1更新边<u,v+n>容量上界

6> 如果u行v列的数字必须等于w,则边<u,v+n>流量的下界和容量都是w

找到的最大流,就是问题的解

#include <stdio.h>#include <string.h>#define max(a,b) a>b?a:b#define min(a,b) a<b?a:b#define N 240#define INF 0x3fffffffint T,n,m;int flow[N][N],a[N],pre[N],first[N],low[N][N],high[N][N],q[200000];void init(){int i,j;memset(low,0,sizeof(low));memset(flow,0,sizeof(flow));for(i = 0;i<N;i++)for(j = 0;j<N;j++)high[i][j] = INF;}int maxflow(int s,int t){int front,rear,now,i,res=0;while(1){front = rear = -1;q[++rear] = s;memset(a,0,sizeof(a));memset(pre,0,sizeof(pre));a[s] = INF;while(front < rear){now = q[++front];for(i = 0;i<=t;i++)if(!a[i] && flow[now][i]>0){a[i] = min(a[now],flow[now][i]);q[++rear] = i;pre[i] = now;}}if(!a[t])break;res += a[t];for(i = t;i!=s;i=pre[i]){flow[pre[i]][i] -= a[t];flow[i][pre[i]] += a[t];}}return res;}int main(){freopen("a.txt","r",stdin);scanf("%d",&T);while(T--){int i,j,k,a,b,w,s1,s2,t1,t2,supers,supert,flag = 1,sum=0;char ch;init();//输入scanf("%d %d",&n,&m);for(i = 1;i<=n;i++){//每行的数字和scanf("%d",&j);low[0][i] = high[0][i] = j;}for(i = 1;i<=m;i++){//每列的数字和scanf("%d",&j);low[n+i][n+m+1] = high[n+i][n+m+1] = j;}scanf("%d\n",&k);while(k--){//限制条件scanf("%d %d %c %d",&a,&b,&ch,&w);s1 = t1 = a;s2 = t2 = b;if(!a)//如果a是0,表示所有行,更新行的上下界s1 = 1,t1 = n;if(!b)//如果b是0,表示所有列,更新列的上下界s2 = 1,t2 = m;for(i = s1;i<=t1;i++)for(j = s2;j<=t2;j++)if(ch == '>')//表示下界,注意是w+1low[i][j+n] = max(low[i][j+n],w+1);else if(ch == '<')//表示上界,注意是w+1high[i][j+n] = min(high[i][j+n],w-1);else{//表示相等,注意是wlow[i][j+n] = max(low[i][j+n],w);high[i][j+n] = min(high[i][j+n],w);}}//建图,填充flow数组for(i = 1;i<=n;i++)for(j = 1+n;j<=m+n;j++)if(low[i][j]<=high[i][j])flow[i][j] = high[i][j] - low[i][j];else//表示某位置不符合限制条件flag = 0;if(flag){supers = n+m+3;//添加的虚拟源点supert = n+m+2;//添加的虚拟汇点for(i = 0;i<=n+m;i++)for(j = 1;j<=n+m+1;j++)if(low[i][j]){flow[supers][j] += low[i][j];flow[i][supert] += low[i][j];sum += low[i][j];}flow[n+m+1][0] = INF;//一条从汇点到源点的无穷边if(sum == maxflow(supers,supert)){//表示存在可行流,继续求最大流flow[n+m+1][0] = flow[0][n+m+1] = 0;//去掉那条最后添加的虚拟边maxflow(0,n+m+1);for(i = 1;i<=n;i++){for(j = 1+n;j<=n+m;j++)printf("%d ",high[i][j]-flow[i][j]);//最终流的结果是上界-剩余putchar('\n');}putchar('\n');}else//表示不存在可行流flag = 0;}if(!flag)printf("IMPOSSIBLE\n\n");}return 0;}


0 0
原创粉丝点击