【题目大意】
有一n*m的矩阵,但其间的具体数值未知,但知道某些元素值在某个区间内,再给出每行的和及每列的和,求任意一种可行方案,无解输出"IMPOSSIBLE"。(Special Judge)
链接: POJ2396
【思路】
每行每列的和等于总和,考虑网络流(许多流量元素流到一起,汇总到一条边),某个元素有上下界,考虑有上下界的网络流。
流过的流量表示元素的真实数值,元素上下界对应网络流中的上下界,求解有源汇的网络流即可。
【建模方式】
以每以行为一个X点,每一列为一个Y点,建立二分图。附加源点S,汇点T,连边 S -> X 流量范围为[每行的和,每行的和],Y -> T 流量范围为[每列的和,每列的和]。
连边 X -> Y 流量范围为[X行Y列元素下界,X行Y列元素上界]。问题转换为有源汇有上下界的可行流,最后沿残量网络上的边输出结果。
【代码】
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAX_N=22,MAX_M=243,INF=0x3f3f3f3f;
struct Edge{
int from,to,flow,next;
};
class ISAP {
static const int maxm=80010;
static const int maxn=410;
int n,s,t,len;
bool vis[maxn];
int dis[maxn];
int cur[maxn];
int p[maxn];
int num[maxn];
inline bool rev_bfs(void){
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(t);
dis[t]=0;
vis[t]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(register int i=head[x];i;i=edges[i].next){
Edge &e=edges[i^1];
if(!vis[e.from]&&e.flow>0){
vis[e.from]=1;
dis[e.from]=dis[x]+1;
q.push(e.from);
}
}
}
return vis[s];
}
inline int augment(){
int x=t,mx=INF;
while(x!=s){
Edge &e=edges[p[x]];
mx=min(mx,e.flow);
x=e.from;
}
x=t;
while(x!=s){
edges[p[x]].flow-=mx;
edges[p[x]^1].flow+=mx;
x=edges[p[x]].from;
}
return mx;
}
public:
Edge edges[maxm<<1];
int head[maxn];
inline void reset(){
memset(head,0,sizeof(head));
len=1;
}
inline ISAP(void){
memset(head,0,sizeof(head));
len=1;
}
inline void ins(int from,int to,int flow,int rev=0){
edges[++len]=((Edge){from,to,flow,head[from]});
head[from]=len;
edges[++len]=((Edge){to,from,rev,head[to]});
head[to]=len;
}
inline int max_flow(int s,int t,int n){
this->s=s,this->t=t,this->n=n;
int flow=0;
rev_bfs();
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
num[dis[i]]++;
int x=s;
memcpy(cur,head,sizeof(cur));
while(dis[s]<n){
if(x==t){
flow+=augment();
x=s;
}
bool tf=false;
for(register int &i=cur[x];i;i=edges[i].next){
Edge &e=edges[i];
if(e.flow>0&&dis[x]==dis[e.to]+1){
tf=true;
p[e.to]=i;
x=e.to;
break;
}
}
if(!tf){
int m=n-1;
for(register int i=head[x];i;i=edges[i].next){
Edge &e=edges[i];
if(e.flow>0)
m=min(m,dis[e.to]);
}
if(--num[dis[x]]==0)
break;
num[dis[x]=m+1]++;
cur[x]=head[x];
if(x!=s)
x=edges[p[x]].from;
}
}
return flow;
}
}solve;
int low[MAX_M][MAX_M],up[MAX_M][MAX_M],cnt[MAX_M],
row,col,s,t;
bool tfs;
bool change(int u,int v,char ch,int flow) {
if(ch=='='){
if(low[u][v]>flow || up[u][v] < flow)
return false;
low[u][v]=up[u][v]=flow;
}else if(ch=='>')
low[u][v]=max(low[u][v],flow+1);
else
up[u][v]=min(up[u][v],flow-1);
return (low[u][v]<=up[u][v]);
}
void read() {
int flow,num,tn,tm;
char choose;
for(int i=1;i<=row;i++) {
scanf("%d",&flow);
low[s][i]=up[s][i]=flow;
}
for(int i=1;i<=col;i++) {
scanf("%d",&flow);
low[i][t]=up[i][t]=flow;
}
scanf("%d",&num);
while(num--) {
scanf("%d %d %c %d",&tn,&tm,&choose,&flow);
if(tn&&tm&&tfs)
tfs=change(tn,tm,choose,flow);
else if(!tn&&tm&&tfs)
for(int i=1;i<=row;i++)
tfs=change(i,tm,choose,flow);
else if(tn&&!tm&&tfs)
for(int j=1;j<=col;j++)
tfs=change(tn,j,choose,flow);
else if(tfs)
for(int i=1;i<=row;i++)
for(int j=1;j<=col;j++)
tfs=change(i,j,choose,flow);
}
}
bool flows() {
int SS=t+1,TT=t+2,sum=0;
for(int i=1;i<=row;i++)
for(int j=1;j<=col;j++) {
//上下界网络流建边
solve.ins(i,j+row,up[i][j]-low[i][j]);
cnt[i]-=low[i][j],cnt[j+row]+=low[i][j];
}
for(int i=1;i<=row;i++) {
solve.ins(s,i,0);
cnt[s]-=low[s][i];
cnt[i]+=low[s][i];
}
for(int j=1;j<=col;j++) {
solve.ins(j+row,t,0);
cnt[j+row]-=low[j][t];
cnt[t]+=low[j][t];
}
solve.ins(t,s,INF);
for(int i=1;i<=t;i++)
if(cnt[i]>0) {
solve.ins(SS,i,cnt[i]);
sum+=cnt[i];
} else
solve.ins(i,TT,-cnt[i]);
//网络流
int ans;
if((ans=solve.max_flow(SS,TT,TT+5))!=sum)
return false;
else
return true;
}
int main() {
int test;
scanf("%d",&test);
while(test--) {
//清空信息
tfs=true;
memset(cnt,0,sizeof(cnt));
memset(low,0,sizeof(low));
memset(up,INF,sizeof(up));
solve.reset();
//读入
scanf("%d%d",&row,&col);
s=row+col+1,t=s+1;
read();
//网络流
if(tfs)
tfs=flows();
if(!tfs) {
printf("IMPOSSIBLE\n");
continue;
}
//输出
int v,print[MAX_M][MAX_N];
for(int i=1;i<=row;i++) {
int k=1;
for(int j=solve.head[i];j ;j=solve.edges[j].next) {
v=solve.edges[j].to;
if(v>=row+1&&v<=row+col)
print[i][k++]=solve.edges[j^1].flow+low[i][v-row];
}
}
for(int i=1;i<=row;i++) {
for(int j=col;j>0;j--)
printf("%d ",print[i][j]);
printf("\n");
}
printf("\n");
}
return 0;
}