题目链接:哆啦A梦传送门
题意:有n行m列个方格,每个方格有未知的数,现在有这些条件:
第一行n个数,表示方格每行的总和。
第二行m个数,表示方格每列的总和。
接着有q个条件,每个条件为x,y,op,z,表示第x行第y列的值条件为z。
输出满足上述条件的n*m方格,如果没有的话,输出:IMPOSSIBLE。
题解:
无源汇上下界可行流:
首先建立一个源ss和一个汇tt,一般称为附加源和附加汇。
对于图中的每条弧<u,v><u,v>,假设它容量上界为c,下界b,那么把这条边拆为三条只有上界的弧。
一条为<ss,v><ss,v>,容量为b;
一条为<u,tt><u,tt>,容量为b;
一条为<u,v><u,v>,容量为c−b。
其中前两条弧一般称为附加弧。
然后对这张图跑最大流,以ss为源,以tt为汇,如果所有的附加弧都满流,则原图有可行流。
这时,每条非附加弧的流量加上它的容量下界,就是原图中这条弧应该有的流量
有源汇上下界可行流:
建立弧<t,s>,容量下界为0,上界为∞。
然后对这个新图(实际上只是比原图多了一条边)按照无源汇可行流的方法建模,如果所有附加弧满流,则存在可行流。
求原图中每条边对应的实际流量的方法,同无源汇可行流,只是忽略掉弧<t,s>就好。
而且这时候弧<t,s>的流量就是原图的总流量。
该题将每一行和每一列都看作一个点,然后根据输入的约束条件建图,附加源点到每个点的容量为该点入流的所有边的下界和,每个点到附加汇点的容量为该点出流的所有边的下界和。此处我们可以用tre[]来存储每个点入流出流的差值。tre>0要将附加源点与该点建权值为tre的边,tre<0要将该点与附加汇点建权值为-tre的边。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxm=50000;
const int maxn=500;
#define INF 0x3f3f3f3f
struct edge{
int from,to,w,next;
}e[maxm];
int cnt;
int dep[maxn],head[maxn];
int low[maxn][maxn],up[maxn][maxn],tre[maxn];
int n,m;
void init(){
memset(head,-1,sizeof(head));
cnt=0;
memset(tre,0,sizeof(tre));
memset(up,INF,sizeof(up));
memset(low,0,sizeof(low));
}
void add(int u,int v,int w)
{
e[cnt].from=u;
e[cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt++;
e[cnt].from=v;
e[cnt].to=u;
e[cnt].w=0;
e[cnt].next=head[v];
head[v]=cnt++;
}
int bfs(int s,int t)
{
// while(!que.empty()) que.pop();
queue<int> que;
memset(dep,-1,sizeof(dep));
dep[s]=0;
que.push(s);
while(!que.empty()){
int u=que.front();
que.pop();
// if(u==t) return 1;
for(int i=head[u];i!=-1;i=e[i].next){
int w=e[i].w;
int v=e[i].to;
if(w&&dep[v]==-1){
dep[v]=dep[u]+1;
que.push(v);
if(v==t) return 1;
}
}
}
return 0;
}
int dfs(int u,int mi,int t){
if(u==t||mi==0) return mi;
int tmp;
int ret=0;
// for(int v=1;v<=t;v++){
for(int i=head[u];i!=-1;i=e[i].next){
int w=e[i].w;
int v=e[i].to;
if(w&&dep[v]==dep[u]+1&&mi-ret>0){ ///mi-ret>0少一次递归,优化
tmp=dfs(v,min(mi-ret,w),t) ;
if(!tmp) dep[v]=0;
e[i].w-=tmp;
e[i^1].w+=tmp;
ret+=tmp;
if(ret==mi) return ret;
}
}
return ret;
}
int dinic(int s,int t)
{
int ans=0;
while(bfs(s,t)){
ans+=dfs(s,INF,t);
}
return ans;
}
bool built(int n,int m)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
///下界大于上界,必然不存在可行流
if(low[i][j]>up[i][j]) return 0;
tre[i]-=low[i][j]; ///i点的tre值减去容量下界,表示出边
tre[j+n]+=low[i][j];///j+n点的tre值减容量下界,表示入边
///i点到j点建容量为上下界差值的自由流
add(i,j+n,up[i][j]-low[i][j]);
}
}
return 1;
}
/*
0 8 -18
7 1 5
7 2 1
3 8 -2
5 8 -4
7 6 18
*/
void limitflow(int s,int t,int n,int m)
{
///附加源汇点x,y
int x=t+1,y=t+2;
for(int i=0;i<=t;i++)
{
if(tre[i]>0){ ///大于0,表示该点将要流进来的必要弧的总流量,附加源点与它建边
add(x,i,tre[i]);
}
else if(tre[i]<0){///小于0,表示该点将要流出去的必要弧的总流量,它与附加汇点建边
add(i,y,-tre[i]);
}
}
///因为这里是有源汇上下界网络流,我们之前建的是无源汇上下界网络流
///但我们这里只需将汇点与源点建边就行了,这样就可看作无源汇上下界网络流
add(t,s,INF);
int fl=dinic(x,y);
///如果附加源点的边权值不为0,不满足无源汇可行流条件
for(int i=head[x];i!=-1;i=e[i].next){
if(e[i].w){
printf("IMPOSSIBLE\n");
return ;
}
}
int i;
for(i=head[t];i!=-1;i=e[i].next){
if(e[i].to==s) break;
}
if(i==-1){ ///不存在到源点的边则不存在可行流
printf("IMPOSSIBLE\n");return ;
}
///输出可行方案
for(int i=1;i<=n;i++)
{
int j;
for( j=1;j<m;j++)
{
///每条边的反向边+该边的下界
printf("%d ",e[((i-1)*m+j)*2-1].w+low[i][j]);
}
printf("%d\n",e[i*m*2-1].w+low[i][j]);
}
puts("");
return ;
}
int main()
{
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
init();///初始化
n,m;
scanf("%d%d",&n,&m);
int s=0,t=n+m+1; ///源汇点
int sum1=0,sum2=0;///计算行,列总和
for(int i=1;i<=n;i++) ///每行看作一个点
{
int q;
scanf("%d",&q);
tre[s]-=q;///源点流出q。故要减q
tre[i]+=q;///i点流入q,故要加q
sum1+=q;
}
///这里没跟built那里一样建边,可以看成这些边的上下界都一样
for(int i=n+1;i<=m+n;i++) ///每列看成一个点
{
int q;
scanf("%d",&q);
tre[i]-=q; ///i点流出
tre[t]+=q;///汇点流入
sum2+=q;
}
int q;
scanf("%d",&q);
while(q--)
{
int x,y,d;
char op[6];
scanf("%d%d%s%d",&x,&y,op,&d);
int l1=x,l2=x;
int r1=y,r2=y;
if(!x){
l1=1,l2=n;
}
if(!y){
r1=1,r2=m;
}
for(int i=l1;i<=l2;i++) ///更新每个点的上下界
{
for(int j=r1;j<=r2;j++)
{
if(op[0]=='='){
low[i][j]=max(d,low[i][j]);
up[i][j]=min(d,up[i][j]);
}
else if(op[0]=='>'){
low[i][j]=max(d+1,low[i][j]);
}
else{
up[i][j]=min(d-1,up[i][j]);
}
}
}
}
if(sum1==sum2&&built(n,m)) limitflow(s,t,n,m);
else printf("IMPOSSIBLE\n");
}
}