POJ炸了,没测,但ZOJ1994同一题,可以过。
首先是矩阵,每个行和and列和有约束,找每个单元的值,
自然想到网络流,源点指向每个行节点,权值为行和。
每个列节点指向汇点,权值为列和。
行节点指向列节点的边为每个单元,初始权值为INF,所求边流量即为单元的权值。
但是,每个单元也有约束,所有行节点指向列节点的边也有约束。
自然转化成上下界网络流。
上下界网络流求法:\green{上下界网络流求法:}上下界网络流求法:
首先按普通网络流建图,但是,每条边保存两个权值,下界down和上届up
然后再新设两个节点:
超级源点和超级汇点,作用是分离下界权值。
具体做法:
超级源点指向所有原节点,权值为该节点的流入下界和。
所有原节点指向超级汇点,权值为该节点的流出下界和。
所有原边变为上届与下界差。
原源点与原汇点新设双向边,权值都为INF
在新网络图上跑超级源点到超级汇点的最大流。
若超级源点发出的所有边都满载,则存在一个满足所有约束的解。
原理:\blue{原理:}原理:
所有边都只有下界值的流量时,不一定是个可行流量,因为对于一个非源汇节点,流入流出应该是相同的。
新设超级源汇点就是找一个平衡点,最大流在于要使超级源点满载,即所有下界满足。
// ShellDawn
// POJ2396
// No.27
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define MM(x,y) memset(x,y,sizeof(x))
#define INF 0x3f3f3f3f
using namespace std;
#define maxn 250
int s,t,S,T;
int n,m;
// 边判断,上下界
int E[maxn][maxn][2];
// 网络流图
int G[maxn][maxn];
int V[maxn];
void print(){
for(int i=0;i<n+m+4;i++){
printf("ID:%d ",i);
for(int j=0;j<n+m+4;j++){
printf("%d/%d ",E[i][j][0],E[i][j][1]);
}
puts("");
}
puts("");
for(int i=0;i<n+m+4;i++){
printf("ID:%d ",i);
for(int j=0;j<n+m+4;j++){
printf("%d ",G[i][j]);
}
puts("");
}
}
bool BFS(int s){
MM(V,0);
queue<int> q;
q.push(s);
V[s] = 1;
while(!q.empty()){
int now = q.front();
q.pop();
for(int i=0;i<n+m+4;i++){
if(V[i] == 0 && G[now][i] > 0){
V[i] = V[now] + 1;
q.push(i);
}
}
}
if(V[T] > 0) return true;
return false;
}
int DFS(int now,int minflow){
if(now == T) return minflow;
int flow = 0;
for(int i=0;i<n+m+4 && minflow;i++){
if(V[i] == V[now]+1 && G[now][i] > 0){
int f = DFS(i,min(minflow,G[now][i]));
minflow -= f;
flow += f;
G[now][i] -= f;
G[i][now] += f;
}
}
if(flow == 0) V[now] = 0;
return flow;
}
int main(){
//freopen("A.txt","r",stdin);
//freopen("B.txt","w",stdout);
int at;
scanf("%d",&at);
for(int ct = 0;ct<at;ct++){
if(ct != 0) puts("");
MM(E,0);
scanf("%d%d",&n,&m);
// Ri 与 Ci 上限为INF
for(int i=0;i<n;i++){
for(int j=n;j<n+m;j++){
E[i][j][1] = INF;
}
}
// s,t网路流源汇点,S,T为超级源汇点
s = n + m ; t = s + 1; S = t + 1; T = S + 1;
// 源点到Ri上下界为v
for(int i=0;i<n;i++){
int v;
scanf("%d",&v);
E[s][i][0] = E[s][i][1] = v;
}
// Ci到汇点上下界为v
for(int i=n;i<n+m;i++){
int v;
scanf("%d",&v);
E[i][t][0] = E[i][t][1] = v;
}
// 约束条件
int bt;
scanf("%d",&bt);
while(bt--){
int a,b,v;
char c[3];
scanf("%d%d%s%d",&a,&b,c,&v);
if(a!=0 && b!=0){
// 单元
a--;b+=n-1;
if(c[0] == '='){
E[a][b][0] = max(E[a][b][0],v);
E[a][b][1] = min(E[a][b][1],v);
}else if(c[0] == '>'){
E[a][b][0] = max(E[a][b][0],v+1);
}else{
E[a][b][1] = min(E[a][b][1],v-1);
}
}else if(a == 0 && b == 0){
// 全矩阵
if(c[0] == '='){
for(int i=0;i<n;i++){
for(int j=n;j<n+m;j++){
E[i][j][0] = max(E[i][j][0],v);
E[i][j][1] = min(E[i][j][1],v);
}
}
}else if(c[0] == '>'){
for(int i=0;i<n;i++){
for(int j=n;j<n+m;j++){
E[i][j][0] = max(E[i][j][0],v+1);
}
}
}else{
for(int i=0;i<n;i++){
for(int j=n;j<n+m;j++){
E[i][j][1] = min(E[i][j][1],v-1);
}
}
}
}else{
if(a == 0){
// 全行
b+=n-1;
if(c[0] == '='){
for(int i=0;i<n;i++){
E[i][b][0] = max(E[i][b][0],v);
E[i][b][1] = min(E[i][b][1],v);
}
}else if(c[0] == '>'){
for(int i=0;i<n;i++){
E[i][b][0] = max(E[i][b][0],v+1);
}
}else{
for(int i=0;i<n;i++){
E[i][b][1] = min(E[i][b][1],v-1);
}
}
}else{
// 全列
a--;
if(c[0] == '='){
for(int i=n;i<n+m;i++){
E[a][i][0] = max(E[a][i][0],v);
E[a][i][1] = min(E[a][i][1],v);
}
}else if(c[0] == '>'){
for(int i=n;i<n+m;i++){
E[a][i][0] = max(E[a][i][0],v+1);
}
}else{
for(int i=n;i<n+m;i++){
E[a][i][1] = min(E[a][i][1],v-1);
}
}
}
}
}
// 判断矛盾
bool flag = true;
for(int i=0;i<n&&flag;i++){
for(int j=n;j<n+m&&flag;j++){
if(E[i][j][0] > E[i][j][1]) flag = false;
}
}
if(!flag){
puts("IMPOSSIBLE");
continue;
}
// 建立网络流
MM(G,0);
// Ri 到 Cj,净值
for(int i=0;i<n;i++){
for(int j=n;j<n+m;j++){
G[i][j] = E[i][j][1] - E[i][j][0];
}
}
// 源汇点无穷大
G[s][t] = G[t][s] = INF;
// 对于0~n+m+1所有点 建立超级源点
for(int i=0;i<n+m+2;i++){
int sum = 0;
// 汇入下界和
for(int j=0;j<n+m+2;j++) sum += E[j][i][0];
G[S][i] = sum;
// 流出下界和
sum = 0;
for(int j=0;j<n+m+2;j++) sum += E[i][j][0];
G[i][T] = sum;
}
// 计算可行流
int maxflow = 0;
while(BFS(S)) maxflow += DFS(S,INF);
// 判断满载
flag = true;
for(int i=0;i<n+m+2 && flag;i++){
if(G[S][i] != 0) flag = false;
}
// 不满载
if(!flag){
puts("IMPOSSIBLE");
continue;
}
// 输出
for(int i=0;i<n;i++){
for(int j=n;j<n+m;j++){
printf("%d%s",G[j][i] + E[i][j][0],j==n+m-1?"\n":" ");
}
}
}
return 0;
}