这个题的建模方法非常妙
首先处理出每一行和每一列所有元素值的和
先开一个源点和一个汇点,源点连所有的行,所有的列连汇
源点到行的容量为该行元素和,列到汇点同理,每一行到每一列的容量为最大值20,这样跑一个最大流,行i到列j的流量即为答案i,j的值,最后如果有解源点和汇点相邻的边都应该是满流
例如这一行的元素和是16,源点流过来16,再把16分配到每一列去
但这样有个问题就是中间可能有0流
所以就预先先把每个位置填个1,在网络流的网络中相应修改一下容量就可以
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define INF (210000000)
#define fr(i,s,t) for (i=s;i<=t;i++)
using namespace std;
struct edge{
int to,cap,flow;
};
vector <int> vec[200];
vector <edge> edges;
int n,m,a[200],b[200],res[50][50],d[50],cur[50],num;
void addedge(int x,int y,int cap){
edges.push_back((edge){y,cap,0});
edges.push_back((edge){x,0,0});
int m=edges.size();
vec[x].push_back(m-2);
vec[y].push_back(m-1);
}
bool bfs(){
memset(d,0,sizeof(d));
int i,y,x;
queue <int> q;
q.push(0);
while (!q.empty()){
x=q.front(); q.pop();
fr(i,0,vec[x].size()-1)
if (edges[vec[x][i]].cap>edges[vec[x][i]].flow){//开始忘了这个判断,这样就会死循环了
y=edges[vec[x][i]].to;
if (!y||d[y]) continue;
d[y]=d[x]+1;
q.push(y);
}
}
return d[n+m+1];
}
int dfs(int x,int a){
if (x==n+m+1||a==0) return a;
int flow=0,f;
for (;cur[x]<vec[x].size();cur[x]++){
int i=vec[x][cur[x]];//弧的编号
if (d[x]+1==d[edges[i].to]&&(f=dfs(edges[i].to,min(a,edges[i].cap-edges[i].flow)))>0){
edges[i].flow+=f;
edges[i^1].flow-=f;
flow+=f;
a-=f;
if (a==0) break;
}
}
return flow;
}
void work(){
printf("Matrix %d\n",++num);
scanf("%d %d",&n,&m);
int i,j,y;
fr(i,0,n+m+1) vec[i].clear();
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(res,0,sizeof(res));
fr(i,1,n) scanf("%d",&a[i]);
fr(i,1,m) scanf("%d",&b[i]);
for (i=n;i>=1;i--) a[i]-=a[i-1];
for (i=m;i>=1;i--) b[i]-=b[i-1];
fr(i,1,n) addedge(0,i,a[i]-m);
fr(i,1,n) fr(j,n+1,n+m) addedge(i,j,19);
fr(i,n+1,n+m) addedge(i,n+m+1,b[i-n]-n);
while (bfs())
memset(cur,0,sizeof(cur)),dfs(0,INF);
fr(i,1,n)
fr(j,0,vec[i].size()-1){
y=edges[vec[i][j]].to-n;
res[i][y]=edges[vec[i][j]].flow+1;
}
fr(i,1,n){
fr(j,1,m)
printf("%d ",res[i][j]);
cout<<endl;
}
cout<<endl;
}
int main(){
int nn;cin>>nn;
while (nn--) work();
return 0;
}