hdu 4411 Arrest(费用流)

来源:互联网 发布:淘宝关键词工具 编辑:程序博客网 时间:2024/05/21 19:26


题意:有N+1个点,每个点与编号大于自己的点之间有一条有权边(权重通过floyd求得),现有k个人位于0处,要从k个人中选出若干个人遍历其它点并最终回到0点,使每个点(除0外)都被访问恰好一次,问最小费用之和为多少

解法:每个点之多走一次,显然需要把一个点拆成两个,一个出点一个入点之间费用为0流量为1,超级源点拆为流量为k费用为距离的边,但如何让保证每个点都恰好走过一次呢?由于原图无环,所以可以将i和i‘之间的费用设为-M,流量设为1,M应该大于源点和汇点间最长链的长度。由于每次都找最短路径,因此这些边一定会被有限考虑,因此可以保证这些边恰好走了一次。

但是每次次增广后都会产生反向负权边,如何保证不走反向边呢?方法是源点和汇点之间连一条流量为k费用为0的点(貌似也可以枚举k)。

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#include<queue>using namespace std;#define maxn 1010#define maxm 20010const int inf = 0x3f3f3f3f;struct Nod {int b, nxt;int cap, cst;void init(int b, int nxt, int cap, int cst) {this->b = b;this->nxt = nxt;this->cap = cap;this->cst = cst;}};struct MinCost {int E[maxn];int n;Nod buf[maxm * 2];int len;int p[maxn];void init(int n) {this->n = n;memset(E, 255, sizeof(E));len = 0;}void addCap(int a, int b, int cap, int cst) {//printf("%d %d %d\n",a,b,cst);buf[len].init(b, E[a], cap, cst);E[a] = len++;buf[len].init(a, E[b], 0, -cst);E[b] = len++;}bool spfa(int source, int sink) {static queue<int> q;static int d[maxn];memset(d, 63, sizeof(d));memset(p, 255, sizeof(p));d[source] = 0;q.push(source);int u, v;while (!q.empty()) {u = q.front();q.pop();for (int i = E[u]; i != -1; i = buf[i].nxt) {v = buf[i].b;if (buf[i].cap > 0 && d[u] + buf[i].cst < d[v]) {d[v] = d[u] + buf[i].cst;p[v] = i;q.push(v);}}}return d[sink] != inf;}int solve(int source, int sink) {int minCost = 0, maxFlow = 0;//需要maxFlow的话,想办法返回while (spfa(source, sink)) {int neck = inf;for (int t = p[sink]; t != -1; t = p[buf[t ^ 1].b])//buf[t^壹].b是父节点neck = min(neck, buf[t].cap);maxFlow += neck;for (int t = p[sink]; t != -1; t = p[buf[t ^ 1].b]) {buf[t].cap -= neck;//printf("%d\n",buf[t].b);buf[t ^ 1].cap += neck;minCost += buf[t].cst * neck;}//printf("-----\n");}return minCost;}} mc;int map[110][110],n;void floyd() {for (int k =0; k <= n; k++)for (int i =0; i <= n; i++)for (int j=0; j <= n; j++){if (map[i][k] + map[k][j] < map[i][j])map[i][j] = map[i][k]+map[k][j];}}int main() {int m, k, a, b, c;while (scanf("%d%d%d",&n,&m,&k)&&n) {for (int i = 0; i <= n; i++)for (int j = 0;j<= n; j++)map[i][j] = inf;while (m--) {scanf("%d%d%d", &a, &b, &c);map[b][a]=map[a][b] = min(map[a][b],c);}floyd();mc.init(n * 2 + 3);mc.addCap(0, n * 2 + 1, k, 0);mc.addCap(n*2+1,n*2+2,k,0);int temp=1<<23;for (int i = 1; i <= n; i++) {mc.addCap(i, i + n,1,-temp);mc.addCap(n*2+1,i,1,map[i][0]);mc.addCap(i+n, n*2+2,1,map[i][0]);for (int j = i+1;j<=n;j++)mc.addCap(i+n,j,1,map[i][j]);}printf("%d\n",mc.solve(0,n*2+2)+temp*n);}return 0;}

博客搬新家:sensirly.github.io