UVa 11082 & 最大流的行列模型
题意:
给出一个矩阵前i行的和与前j列的和,(i∈[1,r],j属于[1,c]),每个元素ai,j∈[1,20],请你还原出这个矩阵,保证有解.
SOL:
给网络流建模跪了,神一样的建图,如果我我会怎么做呢?...搜索?然而每个元素具有行,列双重相关性...暴力都打不出来吧...
然而我们顺着搜索的方向想,如果每个点的搜索值最终小于这行的和,那么我们应该做什么?增大它!是不是感觉有点增广的想法出来了------>然而我只是瞎BB...事后觉得可以这么想但考场上并不能这么想出来...
考虑它的建图,因为每个元素至少为1,即流量至少为1,那它就变成一个有上下界的网络流问题...不会怎么办?因为每个元素都大于等于1那么我们把他都减一!
我们对每一行的和对应一个点与超级源S相连,容量为这一行的和,对于每条列我们建一个点与汇点相连,容量为列的和,然后行与列的点两两相连,容量为19,也就是数的最大值,然后跑一遍最大流,行与列之间每条边的流量就是原矩阵中的元素.
如何证明它的正确性?
显然所有行与列的和是相等的,即源汇的出入边一定都满流,同时,我们可以把每一行与所有列的连边上的流量看做该行对此列所做的"贡献",因为最大流一定使源汇出入边满流,所以一定有一种方案.那么这个显然是正确的.
发现行与列的关系以及和的限制条件,也许是思路突破的关键吧.
警报!警报!警报!!
自己打的代码莫名TLE了...因为点不多所以老人家写的EK,然而我写的dinic居然TLE了?smg?...因为时间不多先贴上代码,以后有空回来看...模型是关健啊.
TLE代码:
/*========================================================================== # Last modified: 2016-03-08 08:03 # Filename: uva11082.cpp # Description: ==========================================================================*/ #define me AcrossTheSky #include <cstdio> #include <cmath> #include <ctime> #include <string> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <set> #include <map> #include <stack> #include <queue> #include <vector> #define lowbit(x) (x)&(-x) #define FOR(i,a,b) for((i)=(a);(i)<=(b);(i)++) #define FORP(i,a,b) for(int i=(a);i<=(b);i++) #define FORM(i,a,b) for(int i=(a);i>=(b);i--) #define ls(a,b) (((a)+(b)) << 1) #define rs(a,b) (((a)+(b)) >> 1) #define getlc(a) ch[(a)][0] #define getrc(a) ch[(a)][1] #define maxn 1000 #define maxm 1000000 #define pi 3.1415926535898 #define _e 2.718281828459 #define INF 1070000000 using namespace std; typedef long long ll; typedef unsigned long long ull; template<class T> inline void read(T& num) { bool start=false,neg=false; char c; num=0; while((c=getchar())!=EOF) { if(c=='-') start=neg=true; else if(c>='0' && c<='9') { start=true; num=num*10+c-'0'; } else if(start) break; } if(neg) num=-num; } /*==================split line==================*/ struct Edge{ int from,to,cap,v; }e[maxm]; int r,c; int id[maxn][maxn],a[maxn],b[maxn]; int first[maxn],next[maxm],cur[maxn],d[maxn]; bool vis[maxn]; int sume=1,n,S,T; void addedge(int x,int y,int cap){ sume++; e[sume].from=x; e[sume].to=y; e[sume].cap=cap; next[sume]=first[x]; first[x]=sume; id[x][y]=sume; sume++; e[sume].from=y; e[sume].to=x; e[sume].cap=0; next[sume]=first[y]; first[y]=sume; id[y][x]=sume; } void reset(){ sume=1; a[0]=b[0]=0; memset(e,0,sizeof(e)); memset(next,0,sizeof(next)); memset(first,0,sizeof(first)); memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); //T=maxn-10,S=0; } bool bfs(){ queue<int>q; FORP(i,S,T) d[i]=INF; //memset(vis,false,sizeof(vis)); q.push(S); d[S]=0; //vis[S]=true; while (!q.empty()){ int now=q.front(); q.pop(); for (int i=first[now];i;i=next[i]) if (d[e[i].to]==INF && e[i].cap){ d[e[i].to]=d[now]+1; //vis[e[i].to]=true; q.push(e[i].to); } } return d[T]<INF; } int dfs(int now,int a){ if (now==T || !a) return a; int f,flow=0; for (int &i=cur[now];i;i=next[i]) if (d[now]+1==d[e[i].to] && (f=dfs(e[i].to,min(a,e[i].cap)))>0){ flow+=f; a-=f; e[i].cap-=f; e[i].v+=f; e[i^1].cap+=f; if (!a) break; } return flow; } void dinic(){ int flow=0; while (bfs()){ FORP(i,0,n) cur[i]=first[i]; flow+=dfs(S,INF); } } int main(){ int cas; read(cas); //int cass=1; FORP(cass,1,cas){ reset(); scanf("%d%d",&r,&c); n=r+c+1; S=0; T=r+c+4; FORP(i,1,r) scanf("%d",&a[i]);//read(a[i]); FORP(i,1,c) scanf("%d",&b[i]);//read(b[i]); FORP(i,1,r) addedge(0,i,a[i]-a[i-1]-c); FORP(i,1,r) FORP(j,r+1,r+c+1) addedge(i,j,19); FORP(j,r+1,r+c+1) addedge(j,T,b[j-r]-b[j-r-1]-r); printf("Matrix %d\n",cass); //cass++; dinic(); FORP(i,1,r) FORP(j,1,c) printf("%d%c",e[id[i][j+r]].v+1,(j==c)?'\n':' '); if (cas) cout << endl; } }
lrj代码:
// UVa11082 Matrix Decompressing // Rujia Liu // Slower version with EdmondsKarp #include<cstdio> #include<cstring> #include<queue> #include<vector> #include<algorithm> using namespace std; const int maxn = 50 + 5; const int INF = 1000000000; struct Edge { int from, to, cap, flow; Edge(int u, int v, int c, int f):from(u),to(v),cap(c),flow(f) {} }; struct EdmondsKarp { int n, m; vector<Edge> edges; // 边数的两倍 vector<int> G[maxn]; // 邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 int a[maxn]; // 当起点到i的可改进量 int p[maxn]; // 最短路树上p的入弧编号 void init(int n) { for(int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, 0, 0)); m = edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } int Maxflow(int s, int t) { int flow = 0; for(;;) { memset(a, 0, sizeof(a)); queue<int> Q; Q.push(s); a[s] = INF; while(!Q.empty()) { int x = Q.front(); Q.pop(); for(int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(!a[e.to] && e.cap > e.flow) { p[e.to] = G[x][i]; a[e.to] = min(a[x], e.cap-e.flow); Q.push(e.to); } } if(a[t]) break; } if(!a[t]) break; for(int u = t; u != s; u = edges[p[u]].from) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; } flow += a[t]; } return flow; } }; EdmondsKarp g; int no[maxn][maxn]; int main() { int T, R, C, v, kase = 0; scanf("%d", &T); for(int kase = 1; kase <= T; kase++) { scanf("%d%d", &R, &C); g.init(R+C+2); int last = 0; for(int i = 1; i <= R; i++) { scanf("%d", &v); g.AddEdge(0, i, v - last - C); // row sum is v - last last = v; } last = 0; for(int i = 1; i <= C; i++) { scanf("%d", &v); g.AddEdge(R+i, R+C+1, v - last - R); // col sum is v - last last = v; } for(int i = 1; i <= R; i++) for(int j = 1; j <= C; j++) { g.AddEdge(i, R+j, 19); no[i][j] = g.edges.size() - 2; // no[i][j] is the index of arc for cell(i,j) } g.Maxflow(0, R+C+1); printf("Matrix %d\n", kase); for(int i = 1; i <= R; i++) { for(int j = 1; j <= C; j++) printf("%d ", g.edges[no[i][j]].flow + 1); // we subtracted 1 from every cell printf("\n"); } printf("\n"); } return 0; }
Sometimes it s the very people who no one imagines anything of. who do the things that no one can imagine.