题目地址:http://codevs.cn/problem/1033/
分析:
首先说一下建模, 先把每个点拆成两个, 我们称第一个点为 fi, 第二个点位 se, 这样, 对于每一个点, 我们 由fi -> se 连一条容量为1, 花费为 -点权(注意是负的) 的 边(别忘了反边). 然后 对于每一个点的se 向他可以到达的点的fi连一条容量为1花费为0的边.
最后虚拟两个点 st, ed 由st 向第一层的fi 点连一条容量为1, 花费为0 的边, 由最后一层的se 向ed连一条容量为1 花费0 的边.
这样建模就完了.
然后做k次(当然如果没有增广路可以break) 增广算法, 求 最小费用流, 然后输出 相反数就可以AC了
代码:
#include<cstdio>
#include<cstring>
#include<deque>
using namespace std;
#define N 38
#define INF 0x7fffffff
int n, m, k;
int map[N][N];
int dis[N * N * 2];
int incf[N * N * 2], lin[N * N * 2];
bool v[N * N * 2];
deque <int> Q;
struct arr{
int ff, tt, ww, next, cost;
}c[N * N * 2 * N * N];
int st = 0, ed;
int r[N * N * 2];
int inc;
int maxcost = 0;
int tot = 1;
inline void add(int x, int y, int z, int cost){
c[++tot].ff = x; c[tot].tt = y; c[tot].ww = z; c[tot].cost = cost; c[tot].next = r[x]; r[x] = tot;
c[++tot].ff = y; c[tot].tt = x; c[tot].ww = 0; c[tot].cost = -cost;c[tot].next = r[y]; r[y] = tot;
}
bool spfa(){
memset(dis, 0x3f, sizeof(dis));
dis[st] = 0;
Q.clear();
Q.push_back(st);
v[st] = 1;
incf[st] = INF;
while (Q.size()){
int x = Q.front();
Q.pop_front();
v[x] = 0;
for (int i = r[x]; i; i = c[i].next){
int y = c[i].tt;
if (dis[y] > dis[x] + c[i].cost && c[i].ww > 0){
dis[y] = dis[x] + c[i].cost;
incf[y] = min(incf[x], c[i].ww);
lin[y] = i;
if (!v[y]){
v[y] = 1;
if (dis[y] < dis[x]) Q.push_front(y);
else Q.push_back(y);
}
}
}
}
return dis[ed] != 0x3f3f3f3f;
}
inline int num(int x, int y){
x --;
return (x) * m + ((x * x - x) >> 1) + y;
}
void EK(){
int x = ed, y;
while (lin[x]){
y = x; x = c[lin[x]].ff;
c[lin[y]].ww -= incf[ed];
c[lin[y] ^ 1].ww += incf[ed];
}
maxcost += incf[ed] * dis[ed];
return ;
}
int main(){
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
scanf("%d %d %d",&n, &m, &k);
int nn = n;
ed = ((nn * m + ((n * n - n) >> 1)) << 1) + 1;
inc = nn * m + ((n * n - n) >> 1);
for (int i = 1; i <= n; i ++){
for (int j = 1; j <= m + i - 1; j ++){
scanf("%d",&map[i][j]);
add(num(i, j), num(i, j) + inc, 1, - map[i][j]);
}
}
for (int i = 1; i < n; i ++)
for (int j = 1; j <= m + i - 1; j ++){
add(num(i, j) + inc, num(i + 1,j), 1, 0);
add(num(i, j) + inc, num(i + 1, j + 1), 1, 0);
}
for (int i = 1; i <= m; i ++){
add(st, num(1, i), 1, 0);
}
for (int i = 1; i <= n + m - 1; i ++){
add(num(n, i) + inc, ed, 1, 0);
}
for (int i = 1; i <= k; i ++){
if (!spfa()) break;
EK();
}
printf("%d\n", -maxcost);
return 0;
}