题意:有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;
}
博客搬新家: sensirly.github.io