题意
给出一个n*n大小的矩阵,要求从左上角走到右下角,每次只能向下走或者向右走并取数,某位置取过数之后就只为数值0,现在求解从左上角到右下角走K次的最大值.
思路
经典的费用流模型:
K取方格数。 构图方法:将矩阵的每个元素m[i][j]拆成两个点u=(i-1)*n+j和v=n*n+(i-1)*n+j,从u到v连两条边: 1> 连边(u,v),容量为1,费用值为m[i][j],这样可以保证每一个位置的数只被取一次 2> 连边(u,v),容量为INF,费用值为0,这样可以保证某位置取数被置为0之后,随便怎么取对最后的费用值不会产生影响 由于每次只能向下走或者向右走,所以需要连两条边: 1> 向下走:即从(i,j)到(i+1,j),连边v=n*n+(i-1)*n+j到i*n+j,容量为INF,费用为0 2> 向右走:即从(i,j)到(i,j+1),连边v=n*n+(i-1)*n+j到(i-1)*n+j+1,容量为INF,费用为0. 然后为了限制只走K次,需要添加源点s=0和汇点t=2*n*n+1,连边:s……矩阵左上角顶点1,容量为k,费用为0,连边矩阵右下角顶点被拆之后对应的顶点2*n*n……t,容量为K,费用为0,然后对所建立的图求解最大费用流即可.
代码
[cpp] #include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <algorithm> #include <string> #include <queue> #include <cstring> #define MID(x,y) ((x+y)/2) #define MEM(a,b) memset(a,b,sizeof(a)) #define REP(i, begin, m) for (int i = begin; i < begin+m; i ++) using namespace std; const int MAXV = 200005; const int MAXE = 100005; const int oo = 0x3fffffff; template <class NodesType> struct Nodes{ NodesType dist; int pre, head; //pre存前趋边, head存前向星 bool visit; }; template <class EdgesType> struct Edges{ int u, v, next; EdgesType cost, flow; }; template <class T> struct MinCostMaxFlow{ Nodes <T> node[MAXV]; Edges <T> arc[2*MAXE]; int vn, en; void init(int n){ vn = n; en = 0; for (int i = 0; i <= n; i ++){ node[i].head = -1; } } void insert_flow(int u, int v, T flow, T cost){ arc[en].u = u; arc[en].v = v; arc[en].flow = flow; arc[en].cost = cost; arc[en].next = node[u].head; node[u].head = en ++; arc[en].v = u; arc[en].u = v; arc[en].flow = 0; arc[en].cost = -cost; arc[en].next = node[v].head; node[v].head = en ++; } void print_edges(){ for (int i = 0; i < en; i ++){ printf("u = %d v = %d flow = %d cost = %d\n", arc[i].u, arc[i].v, arc[i].flow, arc[i].cost); } } queue <int> q; bool spfa(int s, int t){ for (int i = 1; i <= vn; i ++){ node[i].dist = oo; node[i].pre = -1; node[i].visit = false; } node[s].dist = 0; node[s].visit = true; q.push(s); while(!q.empty()){ int u = q.front(); q.pop(); node[u].visit = false; for (int i = node[u].head; i != -1; i = arc[i].next){ int v = arc[i].v; if (arc[i].flow > 0 && node[v].dist > node[u].dist + arc[i].cost){ node[v].dist = node[u].dist + arc[i].cost; node[v].pre = i; if (!node[v].visit){ node[v].visit = true; q.push(v); } } } } if (node[t].pre == -1) return 0; else return 1; } T solve(int s, int t, T &mincost){ mincost = 0; T maxflow = 0; while(spfa(s, t)){ T minflow = oo; for (int i = node[t].pre; i != -1; i = node[arc[i].u].pre){ minflow = min(minflow, arc[i].flow); } for (int i = node[t].pre; i != -1; i = node[arc[i].u].pre){ arc[i].flow -= minflow; arc[i^1].flow += minflow; mincost += arc[i].cost * minflow; } maxflow += minflow; } return maxflow; } }; MinCostMaxFlow <int> mcmf; int map[55][55]; int main(){ //freopen("test.in", "r", stdin); //freopen("test.out", "w", stdout); int n, k; while(scanf("%d %d", &n, &k) != EOF){ mcmf.init(2*n*n+2); mcmf.insert_flow(2*n*n+1, 1, k, 0); mcmf.insert_flow(2*n*n, 2*n*n+2, k, 0); REP(i, 1, n) REP(j, 1, n){ scanf("%d", &map[i][j]); int u = (i-1)*n+j; mcmf.insert_flow(u, u+n*n, 1, -map[i][j]); mcmf.insert_flow(u, u+n*n, oo, 0); if (i < n){ mcmf.insert_flow(u+n*n, u+n, oo, 0); } if (j < n){ mcmf.insert_flow(u+n*n, u+1, oo, 0); } } //mcmf.print_edges(); int res = 0; mcmf.solve(2*n*n+1, 2*n*n+2, res); printf("%d\n", -res); } return 0; } [/cpp]