Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=2448
【题意】
给定m个station的无向连接图,
再给定m个station上任意的n个vessel(n<=m),
再给定n个port,同时给出station到port的有向连接图,
要求n个vessel回到n个port的最小权值和。
一个port只能容纳一个vessel。
vessel可以在station之间行走。
【思路】
粗略地读了一遍题,没怎么读懂,后来读了一遍,想到了这个思路。
对station进行Floyd算法的运算,求出所有station之间的最短路径。
根据vessel和port建立二分图。左右均为n个端点。
对于每一条station到port的边,枚举每一个vessel,若vessel经过这个station再到达port的路径和小于其原始值(vessel到port),则更新这个值。
这样,插入所有边之后就建立好了二分图。
再运用KM算法,求出最小权值和。
【P.S】
DEBUG之后1A。
发现是2008 Asia Regional Harbin。
【代码】
#include <iostream> using namespace std; #define min(a,b) ((a)<=(b)?(a):(b)) const int maxn = 100;//二分图左端点个数 const int maxm = 100;//二分图右端点个数 const int inf = (1<<30);//定义最大值 int w[maxn+5][maxm+5]; //权值邻接矩阵,初始化为-inf,之后填入权值。 int lx[maxn+5], ly[maxm+5]; int linky[maxm+5];//存储右端点对应的左端点匹配,-1表示无匹配 bool visx[maxn+5], visy[maxm+5]; int lack; bool find(int v, int m) { int i, t; visx[v] = true; for (i=1; i<=m; i++) { if (w[v][i]==-inf || visy[i]) continue; t = lx[v] + ly[i] - w[v][i]; if (t==0) { visy[i] = true; if (linky[i]==-1 || find(linky[i], m)) { linky[i] = v; return true; } } else lack = min(lack, t); } return false; } int KM(int n, int m) { int i, j; for (i=0; i<=m; i++) { ly[i] = 0; linky[i] = -1; } for (i=1; i<=n; i++) { lx[i] =-inf; for (j=1; j<=m; j++) { if (w[i][j]>lx[i]) lx[i] = w[i][j]; } } for (i=1; i<=n; i++) { while(true) { for (j=0; j<=n; j++) visx[j] = false; for (j=0; j<=m; j++) visy[j] = false; lack = inf; if (find(i, m)) break; for (j=1; j<=n; j++) { if (visx[j]) lx[j] -= lack; } for (j=1; j<=m; j++) { if (visy[j]) ly[j] += lack; } } } int ans = 0; for (i=1; i<=m; i++) { if (linky[i]>-1) { ans += w[linky[i]][i]; } } return -ans; } int station[205][205]; int vessel[maxn+5]; void Floyd(int n) { int i, j, k; for (k=1; k<=n; k++) { for (i=1; i<=n; i++) { if (station[i][k]>=inf) continue; for (j=1; j<=n; j++) { if (station[k][j]>=inf) continue; station[i][j] = min(station[i][j], station[i][k]+station[k][j]); } } } } int main() { int n, m, k, p; int a, b, c; int i, j; while(scanf("%d %d %d %d", &n, &m, &k, &p)!=EOF) { for (i=1; i<=n; i++) { scanf("%d", &vessel[i]); } for (i=1; i<=m; i++)//初始化station { station[i][i] = 0; for (j=1; j<i; j++) { station[i][j] = station[j][i] = inf; } } for (i=0; i<k; i++)//建立station { scanf("%d %d %d", &a, &b, &c); station[a][b] = station[b][a] = min(station[a][b], c); } Floyd(m);//Floyd运算 for (i=1; i<=n; i++)//初始化KM权值矩阵 { for (j=1; j<=n; j++) { w[i][j] = inf; } } for (i=0; i<p; i++)//建立矩阵 { scanf("%d %d %d", &b, &a, &c); for (j=1; j<=n; j++) { w[j][b] = min(w[j][b], station[vessel[j]][a]+c); } } for (i=1; i<=n; i++)//求最小权值时应取其相反数 { for (j=1; j<=n; j++) { w[i][j] *= (-1); } } printf("%d\n", KM(n, n));//KM算法运算 } return 0; }