我本来想着很快做对本题的,结果做了将近一个下午,WA了四次,才换来难得的AC,看看还是有总结的必要,从中吸取教训。
给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现在从(1,1)出发,可以往右或者往下走,最后到达(n,n),每达到一格,把该格子的数取出来,该格子的数就变成0,这样一共走K次,现在要求K次所达到的方格的数的和最大
第一行两个数n,k(1<=n<=50, 0<=k<=10)
接下来n行,每行n个数,分别表示矩阵的每个格子的数
一个数,为最大和
3 1
1 2 3
0 2 1
1 4 2
11
每个格子中的数不超过100
这题要求一共走k次,即每个格子最多走k次,每个格子的价值最多获得一次。像这种限制点或边使用次数的题,我们往往将其转化为限制网络中边的流量(点要拆分)来解。纵观题目,n不到50,用网络流的信念应该更加坚定。然而使得到的价值最大,肯定是用最小费用最大流的变体——“最大费用最大流”来求解。凡是在图上走以求得到最大值的题,一般可以考虑dp或”最长路径”之类的东西,但边取的次数(也可能是点)有限制,就可以排除最短路,转而转化成费用流的问题了。
网络流的题难在难以看出来它是网络流。
费用流的模板一套,我们很快搞出最大费用最大流来。但构图才是网络流的最大的难点。像这种点的流量有限制的题,必然要拆点,然而我这个菜鸡居然naive的认为不拆点也可以,于是在我几次WA后居然改成了不拆点的构图=。=,直到最后才恍然大悟,真是too young。
其实讲真挺简单,但是不注重细节带来的就是难忘的WA。
#include
#include
#include
#include
#include
#include
#include
#define N 60
#define INF 0x7fffffff
using namespace std;
queue <int> q;
bool vis[N*N*2];
int n, k, cur = -1, head_p[N*N*2], val[N][N];
int s, t, flow[N*N*2], pre_e[N*N*2], pre_v[N*N*2], dis[N*N*2];
struct Tadj{
int next, obj, cap, cost;
Tadj() {}
Tadj(int next, int obj, int cap, int cost):
next(next), obj(obj), cap(cap), cost(cost) {}
}Edg[N*N*16];
void Insert(int a, int b, int c, int d){
cur ++;
Edg[cur] = Tadj(head_p[a], b, c, d);
head_p[a] = cur;
}
bool Spfa(){
for(int i = s; i <= t; i++) dis[i] = -INF, vis[i] = false;
q.push(s);
dis[s] = 0;
vis[s] = true;
flow[s] = INF;
while(!q.empty()){
int now = q.front(); q.pop();
for(int i = head_p[now]; ~ i; i = Edg[i].next){
int v = Edg[i].obj, c = Edg[i].cap, l = Edg[i].cost;
if(!c) continue;
if(dis[v] < dis[now] + l){
dis[v] = dis[now] + l;
flow[v] = min(flow[now], c);
pre_v[v] = now;
pre_e[v] = i;
if(!vis[v]){
q.push(v);
vis[v] = true;
}
}
}
vis[now] = false;
}
return dis[t] != -INF;
}
int Max_cost_Max_flow(){
int cost = 0;
while(Spfa()){
cost += dis[t] * flow[t];
for(int i = t; i != s; i = pre_v[i]){
Edg[pre_e[i]].cap -= flow[t];
Edg[pre_e[i]^1].cap += flow[t];
}
}
return cost;
}
int main(){
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
scanf("%d", &val[i][j]);
s = 0;
t = n * n * 2 + 1;
for(int i = s; i <= t; i++) head_p[i] = -1;
Insert(s, 1, k, 0);
Insert(1, s, 0, 0);
Insert(n*n*2, t, k, 0);
Insert(t, n*n*2, 0, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++){
Insert((i-1)*n+j, (i-1)*n+j+n*n, 1, val[i][j]);
Insert((i-1)*n+j+n*n, (i-1)*n+j, 0, -val[i][j]);
Insert((i-1)*n+j, (i-1)*n+j+n*n, k-1, 0);
Insert((i-1)*n+j+n*n, (i-1)*n+j, 0, 0);
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++){
if(i < n){
Insert((i-1)*n+j+n*n, i*n+j, k, 0);
Insert(i*n+j, (i-1)*n+j+n*n, 0, 0);
}
if(j < n){
Insert((i-1)*n+j+n*n, (i-1)*n+j+1, k, 0);
Insert((i-1)*n+j+1, (i-1)*n+j+n*n, 0, 0);
}
}
printf("%d\n", Max_cost_Max_flow());
return 0;
}
既然目标是地平线,留给世界的只能是背影。