HDU 2448 Mining Station on the Sea(最小费用最大流, KM算法)

题意:

有n个港口n条船,m个采矿站,船只能在能够通信的采矿站之间或者能够通信的港口和采矿站之间航行,给你能够通信的单位之间的距离,现在有n条船全都要从采矿站返回到n个港口中,每个港口只能容纳一条船且该船进去后不能再出来,求n条船航行的总距离最小为多少。

思路:

首先是最小费用最大流做法,单纯的建完图跑费用流现在在杭电会超时,但是由于最小费用最大流的时间复杂度为O(F*E*logV)[F是流量,E是边数,V是顶点数],且本题朴素建图的边的个数最多会达到1e6多,再乘以流量和logV,时间确实很玄了。所以需要加一个floyd预处理最短路,之后再进行构图连边。

建立源点S,并连边到各个船所在的点,权值为1,费用为0,即表示只有一个船。再建立汇点T,把各个港口连入汇点T,权值为1,费用为0,表示只能停一个船。

之后就需要floyd优化了,不过先看下最朴素的连边:

将m条无向边直接建立起来,权值为inf,费用为其之间距离,表示可以走无数次。然后再将p条从采集点到港口的有向边直接建立起来,将点连入港口,权值为1,费用为其之间距离。

然而会超时,所以需要先通过floyd预处理出各个采集点到各个港口的最短距离,之后再连边建图,从而减少了边数来优化时间复杂度。


还可以用KM算法,同样需要用floyd将每个采矿站到每个港口的最短距离预处理出来,然后再对距离取负建立二分图,跑一次二分图的最小权匹配即可。


代码1:

#include <algorithm>  
#include <iostream>  
#include <string.h>  
#include <cstdio>  
#include <queue>  
using namespace std;  
const int inf = 0x3f3f3f3f;  
const int maxn = 305;  
const int maxm = 200620;  
struct node  
{  
    int v, c, w, next;  
} edge[maxm];  
int no, head[maxn];  
int n, m, k, p; 
int S, T;  
int vis[maxn], dis[maxn];  
queue<int> q;  
int pre[maxn], rec[maxn];  
void init()  
{  
    no = 0;  
    memset(head, -1, sizeof head);  
}  
inline void add(int u, int v, int w, int c)  
{  
    edge[no].v = v; edge[no].w = w;  
    edge[no].c = c; edge[no].next = head[u];  
    head[u] = no++;  
      
    edge[no].v = u; edge[no].w = 0;  
    edge[no].c = -c; edge[no].next = head[v];  
    head[v] = no++;  
}  
bool SPFA()  
{  
    memset(dis, 0x3f, sizeof dis);  
    memset(vis, 0, sizeof vis);  
    while(!q.empty()) q.pop();  
    pre[S] = S; dis[S] = 0;  
    q.push(S); vis[S] = 1;  
    while(!q.empty())  
    {  
        int top = q.front(); q.pop();  
        vis[top] = 0;  
        for(int k = head[top]; k != -1; k = edge[k].next)  
        {  
            if(edge[k].w && dis[edge[k].v] > dis[top]+edge[k].c)  
            {  
                dis[edge[k].v] = dis[top]+edge[k].c;  
                pre[edge[k].v] = top; rec[edge[k].v] = k;  
                if(!vis[edge[k].v])   
                vis[edge[k].v] = 1, q.push(edge[k].v);  
            }  
        }  
    }  
    if(dis[T] == inf) return false;  
    return true;  
}  
pair<int, int> mincost_maxflow()  
{  
    int mincost = 0, maxflow = 0;  
    while(SPFA())  
    {  
        int flow = inf;  
        for(int k = T; k != S; k = pre[k])  
        flow = min(flow, edge[rec[k]].w);  
        maxflow += flow;  
        for(int k = T; k != S; k = pre[k])  
        {  
            mincost += flow*edge[rec[k]].c;  
            edge[rec[k]].w -= flow;  
            edge[rec[k]^1].w += flow;  
        }  
    }  
    return make_pair(maxflow, mincost);  
}
int d[maxn][maxn];
void floyd()
{
	for(int k = 1; k <= m+n; ++k)
	for(int i = 1; i <= m+n; ++i)
	for(int j = 1; j <= m+n; ++j)
	d[i][j] = min(d[i][j], d[i][k]+d[k][j]);
}
void mapping()
{
	int u, v, c;
	memset(d, 0x3f, sizeof d);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d", &u);
		add(S, u, 1, 0);
	}
	for(int i = 1; i <= n; ++i) add(m+i, T, 1, 0);
	for(int i = 1; i <= k; ++i)
	{
		scanf("%d %d %d", &u, &v, &c);
		d[u][v] = d[v][u] = min(d[v][u], c);
	}
	for(int i = 1; i <= p; ++i)
	{
		scanf("%d %d %d", &u, &v, &c);
		d[v][m+u] = min(d[v][m+u], c);
	}
	floyd();
	for(int i = 1; i <= m; ++i) 
	for(int j = 1; j <= n; ++j)
	add(i, m+j, 1, d[i][m+j]);
}
int main()  
{
    while(~scanf("%d %d %d %d", &n, &m, &k, &p))
	{  
		S = 301, T = 302;
		init();
		mapping(); 
	    pair<int, int> pr = mincost_maxflow();  
	    printf("%d\n", pr.second);  
	}
    return 0;  
}


代码2:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <cstdio>

using namespace std;
const int maxn = 305;
const int inf = 0x3f3f3f3f;

int weight[maxn][maxn];
int lx[maxn], ly[maxn];
int visx[maxn], visy[maxn];
int match[maxn];
int slack[maxn];
int n, m, k, p;
int stop[maxn];

int dfs(int u)
{
	visx[u] = 1;
	for(int i = 1; i <= m; ++i)
	{
		if(visy[i]) continue;
		int gap = lx[u] + ly[i] - weight[u][i];
		if(gap == 0)
		{
			visy[i] = 1;
			if(match[i] == -1 || dfs(match[i]))
			{
				match[i] = u;
				return 1;
			}
		}
		else slack[i] = min(slack[i], gap);
	}
	return 0;
}

int KM()
{
	memset(match, -1, sizeof match);
	memset(ly, 0, sizeof ly);
	
	for(int i = 1; i <= n; ++i)
	{
		lx[i] = weight[i][1];
		for(int j = 2; j <= m; ++j)
		lx[i] = max(lx[i], weight[i][j]);
	}
	
	for(int i = 1; i <= n; ++i)
	{
		memset(slack, 0x3f, sizeof slack);
		while(1)
		{
			memset(visx, 0, sizeof visx);
			memset(visy, 0, sizeof visy);
			
			if(dfs(i)) break; 
			
			int key = inf;
			for(int j = 1; j <= m; ++j)
			if(!visy[j]) key = min(key, slack[j]);
			
			for(int j = 1; j <= n; ++j)
			if(visx[j]) lx[j] -= key;
			
			for(int j = 1; j <= m; ++j)
			if(visy[j]) ly[j] += key;
		}
	}
	int ans = 0;
	for(int i = 1; i <= m; ++i)
	if(match[i] != -1) ans += weight[match[i]][i];
	return -ans;
}
int d[maxn][maxn];  
void floyd()  
{  
    for(int k = 1; k <= m+n; ++k)  
    for(int i = 1; i <= m+n; ++i)  
    for(int j = 1; j <= m+n; ++j)  
    d[i][j] = min(d[i][j], d[i][k]+d[k][j]);  
}  
void mapping()  
{  
    int u, v, c;  
    memset(d, 0x3f, sizeof d);
    memset(stop, 0, sizeof stop);
	fill(weight[0], weight[0]+305*301, -inf);
    for(int i = 1; i <= n; ++i)  
    {  
        scanf("%d", &u); 
        stop[u] = 1;
    }
    for(int i = 1; i <= k; ++i)  
    {  
        scanf("%d %d %d", &u, &v, &c);  
        d[u][v] = d[v][u] = min(d[v][u], c);  
    }  
    for(int i = 1; i <= p; ++i)
    {  
        scanf("%d %d %d", &u, &v, &c);  
        d[v][m+u] = min(d[v][m+u], c);  
    }
    floyd();
    for(int i = 1; i <= m; ++i)
    if(stop[i])
    {
    	for(int j = 1; j <= n; ++j)
    	weight[j][i] = -d[i][m+j];
    }
}
int main()
{
	while(~scanf("%d %d %d %d", &n, &m, &k, &p))  
    {
        mapping();  
        printf("%d\n", KM());    
    }   
	return 0;
}


继续加油~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值