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;
}