标签:
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