题意:有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 20010 const 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; }