参考:http://hi.baidu.com/%CD%F4%BD%ADwangjiang/blog/item/aa3bc8ec3d549edbb31cb1a3.html
题意:现在有一个n*m的方阵,方阵里面的数字未知,但是我们知道如下约束条件:
1> 每一行的数字的和
2> 每一列的数字的和
3> 某些格子有特殊的大小约束,用大于号,小于号和等于号表示
求解是否存在在满足所有的约束的条件下用正数来填充该方阵的方案,若有,输出填充后的方阵,否则输出IMPOSSIBLE.
这道题可以转化成容量有上下界的最大流问题,将方阵的行从1……n编号,列n+1……n+m编号,添加源点s=0和汇点t=n+m+1.
1> 将源点和每一个行节点相连,相连所形成的边的容量的上下界都置为该行所有数字的和
2> 将每一个列节点和汇点相连,相连所形成的边的容量的上下界都置为该列所有数字的和
3> 如果u行v列的数字大于w,则将行节点u和列节点v+n相连,w为该边容量的下界,即置low[u][v+n]=max(low[u][v+n],w+1)
4> 如果u行v列的数字小于w,则将行节点u和列节点v+n相连,w为该边容量的上界,即置high[u][v+n]=min(high[u][v+n],w-1)
5> 如果u行v列的数字等于w,则将行节点u和列节点v+n相连,w既为该边容量的下界同时也为该边容量的上界,即置low[u][v+n]=high[u][v+n]=w
可以将每列看成一个结点,每行看成一个结点,给每行,每列各建立一个节点。
设源点s与每个行节点连接(流量的上下界限制为每行的和),每个列节点与汇点t连接(流量的上下界限制为每列的和)。
从每个行节点流出col条边,流量的上下界从input的条件中获取,同理,每个列节点,有row条边流入。
这样如果存在最大流,一定是唯一的行和或列和,即符合条件限制。那么某行某列的两结点间的边的容量就是该行该列元素上下界容量(即元素的限制)之差。。这样最大流后确保满足题中行列和性质。再新建一个源点ss和汇点tt,那么ss到每个结点的边下界就是该结点入边下界容量和,各点到tt边的下界容量为该结点出边下界容量和。将t和s间边一条无穷容量的边,使之成为无源汇的上下界的流量图。
对新网络求最大流,若所有从源点指出的边均满流,则原网络有解,否则无解;
若有解,再去掉t与s的边,去掉ss和tt后求最大流,那第i行j列的元素的值就是对应两点间流量(即现在求的流量加上下界流量,因为在第一次求最大流时,下界流量丢失,加回来)。
#include<iostream>
#include<string>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
const int MAXN=255;
const int INF=1000000000;
int cap[MAXN][MAXN],flow[MAXN][MAXN],low[MAXN][MAXN],high[MAXN][MAXN];
//cap[][]网络中的容量,flow[][]流量分配,low[][],high[][]流量上下界
int r[MAXN],c[MAXN],q[MAXN],in[MAXN],out[MAXN],d[MAXN];
//in[u]:u所有入边的流量下界和,out[u]:u所有出边的流量下界和,fa[]:dinic中的层次
bool vist[MAXN];
void Init()
{
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
memset(low,0,sizeof(low));
memset(cap,0,sizeof(cap));
memset(high,0,sizeof(high));
memset(flow,0,sizeof(flow));
}
bool bfs(int s,int t,int n)//建立层次网络
{
int i,tmp;
memset(d,-1,sizeof(d));
queue<int>Q;
Q.push(s);
d[s]=0;
while(!Q.empty())
{
tmp=Q.front();
Q.pop();
for(i=0;i<n;i++)
if(cap[tmp][i]>0&&d[i]==-1)
{
d[i]=d[tmp]+1;
Q.push(i);
}
}
if(d[t]!=-1)return true;
else return false;
}
int dfs(int k,int t,int n,int sum)//扩展流量
{
if(k==t)return sum;
int i,os=sum;
for(i=0;i<n;i++)
if(d[i]==d[k]+1&&cap[k][i]>0)
{
int a=dfs(i,t,n,min(sum,cap[k][i]));
cap[k][i]-=a;
cap[i][k]+=a;
flow[k][i]+=a;
flow[i][k]-=a;
sum-=a;
}
return os-sum;
}
int dinic(int s,int t,int n)
{
int ans=0;
while(bfs(s,t,n))
ans+=dfs(s,t,n,INF);
return ans;
}
void solve(int s,int t,int num,int n,int m) //num为节点个数,n*m方阵
{
int i,j,ss,tt,res,sum=0;
ss=num;
tt=num+1;
for(i=0;i<num;i++)
for(j=0;j<num;j++)
{
cap[i][j]=high[i][j]-low[i][j]; //该边容量为上下界流量之差
out[i]+=low[i][j]; //统计点i所有出边的流量下界之和
in[j]+=low[i][j]; //统计点j所有入边的流量下界之和
sum+=low[i][j]; //所有边的流量下界之和
}
for(i=0;i<num;i++) //添加附加源点ss和汇点tt建立附加网络
{
cap[ss][i]=in[i];
cap[i][tt]=out[i];
}
cap[t][s]=INF; //在附加网络中连边cap[t][s]=INF
res=dinic(ss,tt,num+2); //对附加网络求解最大流
if(res!=sum) //附加网络的最大流不等于所有下界之和,则无解
{
printf("IMPOSSIBLE\n");
return;
}
cap[t][s]=cap[s][t]=0; //去掉边(t,s)和(s,t)
res=dinic(s,t,num); //在对去边及超级汇源之后的图求解最大流
for(i=1;i<=n;i++) //输出方阵
{
printf("%d",flow[i][1+n]+low[i][1+n]);
for(j=2;j<=m;j++)
printf(" %d",flow[i][j+n]+low[i][j+n]);
printf("\n");
}
}
int main()
{
int i,j,s,t,u,v,w,m,n,num,ans,ncase;
char ch;
scanf("%d",&ncase);
while(ncase--)
{
Init();
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
scanf("%d",&r[i]);
for(i=1;i<=m;i++)
scanf("%d",&c[i]);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
high[i][j+n]=INF;
scanf("%d",&ans);
while(ans--)
{
scanf("%d %d %c %d",&u,&v,&ch,&w);
if(u==0&&v!=0) //建图
{
if(ch=='=')
{
for(i=1;i<=n;i++)
low[i][v+n]=high[i][v+n]=w;
}
else if(ch=='<')
{
for(i=1;i<=n;i++)
high[i][v+n]=min(high[i][v+n],w-1);
}
else if(ch=='>')
{
for(i=1;i<=n;i++)
low[i][v+n]=max(low[i][v+n],w+1);
}
}
else if(u!=0&&v==0)
{
if(ch=='=')
{
for(i=1;i<=m;i++)
low[u][i+n]=high[u][i+n]=w;
}
else if(ch=='<')
{
for(i=1;i<=m;i++)
high[u][i+n]=min(high[u][i+n],w-1);
}
else if(ch=='>')
{
for(i=1;i<=m;i++)
low[u][i+n]=max(low[u][i+n],w+1);
}
}
else if(u==0&&v==0)
{
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
if(ch=='=')
low[i][j+n]=high[i][j+n]=w;
else if(ch=='<')
high[i][j+n]=min(high[i][j+n],w-1);
else if(ch=='>')
low[i][j+n]=max(low[i][j+n],w+1);
}
}
else if(u!=0&&v!=0)
{
if(ch=='=')
low[u][v+n]=high[u][v+n]=w;
else if(ch=='<')
high[u][v+n]=min(high[u][v+n],w-1);
else if(ch=='>')
low[u][v+n]=max(low[u][v+n],w+1);
}
}
s=0; //源
t=n+m+1; //汇
for(i=1;i<=n;i++)
low[s][i]=high[s][i]=r[i];
for(i=1;i<=m;i++)
low[i+n][t]=high[i+n][t]=c[i];
solve(s,t,t+1,n,m);
printf("\n");
}
return 0;
}