做了无数次最大流 这次终于AC了。。。 每次都RE 真是崩溃!
计算得到最多金矿 就是cost为负数 变相求最小费用最大流
3 2
1 2 3
0 0 0
1 4 2
其实他第一次肯定走1->2->5->8->9 得到 9 你可能会疑惑这不就是贪心吗
这样下去下次最多得到3 但实际上输出是得到4 为啥?
因为走第一次之后他设置了退边的流量为min 使得它可以退回
即1->4->7->8->5->2->3->6->9 这是其中一条路 具体哪条要看进队列顺序
所以其实这样不是贪心 贪心不能做这题的 这题有点动规的意思
首先 讲每个点分为两点A,B 然后搭建4条边,一条A->B 费用为金矿数 容量为1的边 这代表这个金矿拿了一次就不能拿了
然后这条边的退变 B->A 费用为-金矿数, 容量为1, 代表每走一次就要把刚才拿到的金矿数吐出来(这是增广路的定义)
第三条是 A->B 费用为0, 容量为k 的边 这代表可以有很多次这条边,但拿不到金矿,有时候虽然这条边没金矿但是可能要借助这条边走到有金矿的地方
第四条就是他的退边。。。
建立一个起点s, 终点t。。。 然后求s->t 的最小费用即可。。
因为费用使用了负数。。。故求最小费用的相反数 就是 金矿的最大数!
#include <stdio.h> #include <string.h> #include <queue> using std::queue; const int MAXN = 55*55*2, INF = 0x3f3f3f3f; int dis[MAXN], vis[MAXN], pre[MAXN], count[MAXN], head[MAXN]; queue<int> q; int cnt, tn, k, s, t; struct node { int u, v, w, c, next; }e[MAXN*10]; void addEdge(int u, int v, int w, int c) //建立边与它的退边 注意边的++cnt一定要为偶数, 退边的就是紧跟的奇数 e[i] e[i^1]才能相对应起一对正反向边 { e[++cnt].u = u; e[cnt].v = v; e[cnt].w = w; e[cnt].c = c; e[cnt].next = head[e[cnt].u]; head[e[cnt].u] = cnt; e[++cnt].u = v; e[cnt].v = u; e[cnt].w = -w; e[cnt].c = 0; e[cnt].next = head[e[cnt].u]; head[e[cnt].u] = cnt; } int spfa(int src, int des) { int i; memset(vis, 0, sizeof(vis)); memset(count, 0, sizeof(count)); for(i=0; i<=t; i++) dis[i] = INF; dis[0] = 0; while(!q.empty()) q.pop(); q.push(src); vis[src] = 1; pre[src] = -1; while(!q.empty()) { int now = q.front(); q.pop(); for(i=head[now]; i!=-1; i=e[i].next) { int v = e[i].v; if(e[i].c > 0 && dis[v] > dis[now] + e[i].w) //若有容量且能松弛操作 { dis[v] = dis[now] + e[i].w; pre[v] = i; //pre数组记录的是边i 不是点! if(!vis[v]) //若没在队列就加入, 已在队列的话等他v取出的时候他肯定是多次松弛之后的最优值 { vis[v] = 1; q.push(v); if(count[v]++ > t) //存在环 return -1; } } } vis[now] = 0; //有可能等下还会有对now的松弛操作 } if(dis[des] == INF) return -1; else return 1; } int maxFlow(int src, int des) { int i, min = INF, sum = 0; while(spfa(src, des) != -1) { for(i=pre[des]; i!=-1; i=pre[e[i].u]) //算出得到路径的边中容量最小的,则只能使用最小值。。。 这道题里面肯定为1 { min = e[i].c <= min ?e[i].c: min ; } for(i=pre[des]; i!=-1; i=pre[e[i].u]) { e[i].c -= min ; e[i^1].c += min; sum += e[i].w; } } return sum; } int main() { int i, weight; cnt = -1; scanf("%d %d", &tn, &k); s = 0, t = tn*tn*2 + 1; for(i=0; i<=t; i++) head[i] = -1; addEdge(s, 1, 0, k); //对起点s建边 for(i=1; i<=tn*tn; i++) { scanf("%d", &weight); addEdge(i*2-1, i*2, -weight, 1); addEdge(i*2-1, i*2, 0, k); } for(i=1; i<=tn*tn; i++) { if(i%tn != 0) addEdge(i*2, i*2+1, 0, k); if(i <= (tn-1)*tn) addEdge(i*2, (i+tn)*2-1, 0, k); } addEdge(t-1, t, 0, k); //对终点t建边 printf("%d\n", -maxFlow(s, t)); return 0; }