题意:
给你前 i 行和 和前 j 列和 ,还原出这个矩阵
解题思路
首先处理出 每行和 以及 每列和
如果矩阵中的每个数都减一,那么每一行之和都会减少C,每一列都会减少R,这样每个元素的范围变成了0~19。设每一行减一之和为 c[i] ,每一列减一之和为 d[j]。建立一个二分图,每行对应一个X节点,每一列对应一个Y节点,从原点到每一个X节点引一条弧(假设为第一类弧),容量为 c[i] ;从每一个Y节点到汇点引一条弧(假设为第二类弧),容量为 d[j] ;从每一个X节点到每一个Y节点引一条弧(第三类弧),容量为19,。跑一遍最大流,如果第一类弧和第二类弧都满载(流量等于容量),那么每个从X节点到Y节点弧的流量就是矩阵中的元素。
为什么这样是正确的呢?
首先注意到这一点:矩阵中的每个元素都减一。为什么要减一?
这是为了求最大流的方便,题目要求矩阵中的元素要在1到20之间,而在求最大流的时候是有可能出现0流的,所以这样减去一之后求出的流量(矩阵中的元素)在0到19之间,输出时加一就可以了。
那这样建图和求解的原理是什么?
对于每一个X节点,都是只有一个入流,多个出流(分别流向每一个Y节点),显然多个出流的和正好等于入流;同理,对于每一个Y节点,有多个入流(分别来自每一个X节点),只有一个出流,显然多个入流的和等于出流。
再考虑每一类弧的意义:第一类弧,以每一行所有元素之和为容量;第三类弧,以每一列所有元素之和为容量;第二类弧,最终流量为矩阵中的每一个元素。
也就是说,第一类弧分成多个分支,每一个第一类弧都分出一个分支汇到同一个第三类弧。再考虑矩阵:把每一行的和分成多个元素,每一行的和都会分出一个元素排列在同一列,组成这一列的和。其中有很大的相似性,其实就是问题的变形,只要求出每一个第二类弧的流量,就是求出了矩阵中的每一个元素。
代码
大家用的都是刘汝佳的模板,让我这个用挑战程序设计竞赛模板的人心很累啊,感觉自己创造了一个新世界 T_T,完全不能对照着大佬的代码改。。。。。。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <string>
#include <map>
#include <set>
#include <queue>
#include <vector>
#include <stack>
#include <cctype>
using namespace std;
typedef unsigned long long ULL;
const int maxn = 300;
const int INF = 0x3f3f3f3f;
bool used[maxn*2];
int n,m;///n行
struct edge{
int to,cap,rev;
};
vector <edge> G[maxn];
void addedge(int from,int to,int cap){
G[from].push_back((edge){to,cap,G[to].size()});
G[to].push_back((edge){from,0,G[from].size()-1});
}
int dfs(int v,int t,int f){
if(v==t) return f;
used[v] = 1;
for(int i =0;i<G[v].size();++i){
edge &e = G[v][i];
if(!used[e.to] &&e.cap>0){
int d = dfs(e.to,t,min(f,e.cap));
if(d>0){
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
void max_flow(int x,int s,int t){
while(1){
memset( used,0,sizeof(used));
int f = dfs(s,t,INF);
if(f==0) break;
}
printf("Matrix %d\n",x);
for(int i = 1; i<=n; ++i){
for(int j = 1; j<G[i].size();++j){///编号为0的边是由行指向原点的那个边
edge &e = G[i][j];
printf("%d",G[e.to][e.rev].cap+1);
if(j<G[i].size()-1) printf(" ");
else puts("");
}
}
}
int main() {
int t;
scanf("%d",&t);
for(int tt = 1; tt<=t;++tt){
for(int i = 0;i<maxn;++i){
G[i].clear();
}
int sumai[maxn],ai[maxn];
int sumbi[maxn],bi[maxn];
sumai[0] = sumbi[0] = 0;
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;++i){
scanf("%d",&sumai[i]);
ai[i] = sumai[i]-sumai[i-1] - m;
}
for(int i = 1;i<=m;++i){
scanf("%d",&sumbi[i]);
bi[i] = sumbi[i]-sumbi[i-1] - n;
}
///起点向行连边
for(int i = 1; i <= n; ++i){
addedge(0,i,ai[i]);
}
///列向终点连边
for(int i = 1; i <= m; ++i){
addedge(n+i,n+m+1,bi[i]);
}
///行向列连边
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= m; ++j){
addedge(i,n+j,19);
}
}
max_flow(tt,0,n+m+1);
}
return 0;
}
/*
2
3 4
10 31 58
10 20 37 58
3 4
10 31 58
10 20 37 58
*/