标签:
2 10 3 5 10 3 10 3 3 2 5 3 6 7 10 5 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9
28 46 80
题意:
给你一个N*N的矩阵,每个元素代表该处的权值。要求每个点只能走一次,左上角和右下角可以走两次但该处的权值只能获取一次。问你从左上角走到右下角(只能向下或右移动),再从右下角回到左上角(只能向上或左移动)所能得到的最大权值。
解析:
题目中问从左上角走到右下角,再从右下角回到左上角所能得到的最大权值,我们可以转化为从【左上角起点】到【右下角终点】走两次所获的最大权值。题目要求起点终点可以走多次,其他定只能走一次。按这种思路的话起点和终点的权值我们多算了一次,最后结果要减去。
把图看成 n * n个点,相互可到达的点之间建边, 可走次数作为边的容量,权值当做边的费用。问题就变成了最大费用最大流。
建图过程:
把每个每个点 i 拆成左点 i 和右点 i + n * n。虚拟一个超级源点 outset = 0, 超级汇点 inset = 2 * n * n + 1。
(1)当i 为起点或终点时,左点向右点建边,边的容量为2, 因为起点终点可以走两次, 边的费用为点的权值。
(若起点和终点拆成容量为1的边,那我们在起点的时候就只有一个选择:向上走或者向下走,在终点时只能选择是由上面的点传到终点还是由左边的点传入终点,这样的话就相当于求走一次的最大权值和,显然这样建图是不对的。)
(2)当i 不为起点或终点时,左点向右点建边,边的容量为1, 因为只能走一次,边的费用为点的权值。
(3)相互可到达的点之间建边,如 u --> v,建边时 右点u 和左点 v 建边,边的容量最小为1,边的费用为0。这点要想明白!!!
(4)源点向起点的左点建边,边的容量为2,边的费用为0,终点的右点向汇点建边,边的容量为2,费用为0。
这一类的题给你一个N*M的矩阵,对应N*M个点,且每个点都有一定的点权。当第一次到达某个位置时,我们可以获得该位置的点权且只能获取这一次。
这里可以分为两种情况:
(1)要求是每个点只能走一次(除了起点和终点可以走多次),问你从左上角到右下角走两次所获取的最大权值和。
(2)要求是每个点可以走多次,问你从左上角到右下角走K次所获取的最大权值和。
情况(1)应该都可以用这种方式建图。
情况(2)有POJ 3422题,POJ3422。
建图步骤有什么不理解的地方,可以看这篇博客:小比的博客。里面还讲了情况(2)。(自己画了一上午图才想明白,早知道看小比的博客了,能省不少时间,有巨人的肩膀可以站就是好)。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #define INF 0x3f3f3f3f #define maxn 800000 + 1000 #define maxm 4000000 + 1000 using namespace std; int n; int outset; int inset; struct node { int u, v, cap, flow, cost, next; }; node edge[maxm]; int head[maxn], cnt; int per[maxn]; int dist[maxn], vis[maxn]; int map[660][660]; void init(){ cnt = 0; memset(head, -1, sizeof(head)); } void add(int u, int v, int w, int c){ node E1 = {u, v, w, 0, c, head[u]}; edge[cnt] = E1; head[u] = cnt++; node E2 = {v, u, 0, 0, -c, head[v]}; edge[cnt] = E2; head[v] = cnt++; } int change(int x, int y){ return (x - 1) * n + y; } void getmap(){ int t = n * n; outset = 0; inset = n * n * 2 + 1; for(int i = 1; i <= n; ++i){ for(int j = 1; j <= n; ++j){ scanf("%d", &map[i][j]); if(i == 1 && j == 1 || i == n && j == n) add(change(i, j), change(i, j) + t, 2, map[i][j]); else add(change(i, j), change(i, j) + t, 1, map[i][j]); if(i + 1 <= n) add(change(i, j) + t, change(i + 1, j), 1, 0); if(j + 1 <= n) add(change(i, j) + t, change(i, j + 1), 1, 0); } } add(outset, 1, 2, 0); add(change(n, n) + t, inset, 2, 0); } bool SPFA(int st, int ed){ queue<int>q; for(int i = 0; i <= inset; ++i){ dist[i] = -INF; vis[i] = 0; per[i] = -1; } dist[st] = 0; vis[st] = 1; q.push(st); while(!q.empty()){ int u = q.front(); q.pop(); vis[u] = 0; for(int i = head[u]; i != -1; i = edge[i].next){ node E = edge[i]; if(dist[E.v] < dist[u] + E.cost && E.cap > E.flow){ dist[E.v] = dist[u] + E.cost; per[E.v] = i; if(!vis[E.v]){ vis[E.v] = 1; q.push(E.v); } } } } return per[ed] != -1; } void MCMF(int st, int ed, int &cost, int &flow){ flow = 0; cost = 0; while(SPFA(st, ed)){//每次寻找花销最小的路径 int mins = INF; for(int i = per[ed]; i != -1; i = per[edge[i ^ 1].v]){ mins = min(mins, edge[i].cap - edge[i].flow); } //增广 for(int i = per[ed]; i != -1; i = per[edge[i ^ 1].v]){ edge[i].flow += mins; edge[i ^ 1].flow -= mins; cost += edge[i].cost * mins; } flow += mins; } } int main (){ while(scanf("%d", &n) != EOF){ init(); getmap(); int cost, flow; MCMF(outset, inset, cost, flow); cost = cost - map[1][1] - map[n][n]; printf("%d\n", cost); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
HDU 3376--Matrix Again【最大费用最大流 && 经典建图】
标签:
原文地址:http://blog.csdn.net/hpuhjh/article/details/48049147