hdoj 3488 Tour 最小费用最大流KM算法

Posted mthoutai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdoj 3488 Tour 最小费用最大流KM算法相关的知识,希望对你有一定的参考价值。


Tour

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 2299    Accepted Submission(s): 1151


Problem Description
In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 30000) one-way roads connecting them. You are lucky enough to have a chance to have a tour in the kingdom. The route should be designed as: The route should contain one or more loops. (A loop is a route like: A->B->……->P->A.)
Every city should be just in one route.
A loop should have at least two cities. In one route, each city should be visited just once. (The only exception is that the first and the last city should be the same and this city is visited twice.)
The total distance the N roads you have chosen should be minimized.
 

Input
An integer T in the first line indicates the number of the test cases.
In each test case, the first line contains two integers N and M, indicating the number of the cities and the one-way roads. Then M lines followed, each line has three integers U, V and W (0 < W <= 10000), indicating that there is a road from U to V, with the distance of W.
It is guaranteed that at least one valid arrangement of the tour is existed.
A blank line is followed after each test case.
 

Output
For each test case, output a line with exactly one integer, which is the minimum total distance.
 

Sample Input
1 6 9 1 2 5 2 3 5 3 1 10 3 4 12 4 1 8 4 6 11 5 4 7 5 6 9 6 5 4
 

Sample Output
42
 



用费用流G++能够卡过,C++会超时。

用KM应该非常快吧,如今先刷费用流,过几天再好好做KM。


题意:给出n个点m条单向边边以及经过每条边的费用,让你求出走过一个哈密顿环(除起点外,每一个点仅仅能走一次)的最小费用。题目保证至少存在一个环满足条件,事实上推断成环仅仅须要推断是否满流就可以。



思路: 把每一个点i拆分成左点i和右点i+N
1。超级源点连左点。容量为1,费用为0
2。全部右点连超级汇点,容量为1。费用为0
3,每条单向边—— 起点左点 连 终点右点 容量为1,费用为边权。

最后跑一下费用流即可了。

 

 

注意重边的处理,不去重费用流会超时,还有要用G++提交。  抽出时间再补上KM的AC代码。

 

 

费用流AC代码:

 

#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <algorithm>
#define MAXN 400+10
#define MAXM 70000+10
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, cost, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int pre[MAXN], dist[MAXN];
bool vis[MAXN];
int N, M;
int source, sink;
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)//必须去重!!!

{ int i; for(i = head[u]; i != -1; i = edge[i].next) { if(edge[i].to == v) break; } if(i != -1) { if(edge[i].cost > c) edge[i].cost = c, edge[i^1].cost = -c; return ; } Edge E1 = {u, v, w, 0, c, head[u]}; edge[edgenum] = E1; head[u] = edgenum++; Edge E2 = {v, u, 0, 0, -c, head[v]}; edge[edgenum] = E2; head[v] = edgenum++; } void getMap() { scanf("%d%d", &N, &M); int a, b, c; source = 0, sink = 2 * N + 1; //把每一个点i拆分成左点i和右点i+N //超级源点连左点。容量为1,费用为0 //全部右点连超级汇点。容量为1,费用为0 //单向边: 起点左点连终点右点 容量为1,费用为边权 for(int i = 1; i <= N; i++) addEdge(source, i, 1, 0), //addEdge(i, i + N, 1, 0), addEdge(i + N, sink, 1, 0); while(M--) { scanf("%d%d%d", &a, &b, &c); addEdge(a, b+N, 1, c); } } bool SPFA(int s, int t) { queue<int> Q; memset(dist, INF, sizeof(dist)); memset(vis, false, sizeof(vis)); memset(pre, -1, sizeof(pre)); dist[s] = 0; vis[s] = true; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); vis[u] = false; for(int i = head[u]; i != -1; i = edge[i].next) { Edge E = edge[i]; if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow) { dist[E.to] = dist[u] + E.cost; pre[E.to] = i; if(!vis[E.to]) { vis[E.to] = true; Q.push(E.to); } } } } return pre[t] != -1; } void MCMF(int s, int t, int &cost, int &flow) { flow = cost = 0; while(SPFA(s, t)) { int Min = INF; for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { Edge E = edge[i]; Min = min(Min, E.cap - E.flow); } for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) { edge[i].flow += Min; edge[i^1].flow -= Min; cost += edge[i].cost * Min; } flow += Min; } } int main() { int t; scanf("%d", &t); while(t--) { init(); getMap(); int cost, flow; MCMF(source, sink, cost, flow); printf("%d\n", cost); } return 0; }


 

 KM算法:重刷


边权取负。注意重边的处理。


AC代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
#define MAXN 210
using namespace std;
int lx[MAXN], ly[MAXN];
int Map[MAXN][MAXN];
bool visx[MAXN], visy[MAXN];
int slack[MAXN];
int match[MAXN];
int N, M;
void getMap()
{
    for(int i = 1; i <= N; i++)
    {
        for(int j = 1; j <= N; j++)
            Map[i][j] = -INF;
    }
    int a, b, c;
    while(M--)
    {
        scanf("%d%d%d", &a, &b, &c);
        if(-c > Map[a][b])
            Map[a][b] = -c;
    }
}
int DFS(int x)
{
    visx[x] = true;
    for(int y = 1; y <= N; y++)
    {
        if(visy[y]) continue;
        int t = lx[x] + ly[y] - Map[x][y];
        if(t == 0)
        {
            visy[y] = true;
            if(match[y] == -1 || DFS(match[y]))
            {
                match[y] = x;
                return 1;
            }
        }
        else if(slack[y] > t)
            slack[y] = t;
    }
    return 0;
}
void KM()
{
    memset(match, -1, sizeof(match));
    memset(ly, 0, sizeof(ly));
    for(int x = 1; x <= N; x++)
    {
        lx[x] = -INF;
        for(int y = 1; y <= N; y++)
            lx[x] = max(lx[x], Map[x][y]);
    }
    for(int x = 1; x <= N; x++)
    {
        for(int i = 1; i <= N; i++)
            slack[i] = INF;
        while(1)
        {
            memset(visx, false, sizeof(visx));
            memset(visy, false, sizeof(visy));
            if(DFS(x)) break;
            int d = INF;
            for(int i = 1; i <= N; i++)
            {
                if(!visy[i] && slack[i] < d)
                    d = slack[i];
            }
            for(int i = 1; i <= N; i++)
            {
                if(visx[i])
                   lx[i] -= d;
            }
            for(int i = 1; i <= N; i++)
            {
                if(visy[i])
                    ly[i] += d;
                else
                    slack[i] -= d;
            }
        }
    }
    int ans = 0;
    for(int i = 1; i <= N; i++)
        ans += Map[match[i]][i];
    printf("%d\n", -ans);
}
int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &N, &M);
        getMap();
        KM();
    }
    return 0;
}


以上是关于hdoj 3488 Tour 最小费用最大流KM算法的主要内容,如果未能解决你的问题,请参考以下文章

HDU 3488 Tour (最大权完美匹配)KM算法

hdoj 1533 Going Home 最小费用最大流KM入门题

hdu 3488 Tour

HDU3488 Tour —— 二分图最大权匹配 KM算法

HDU3488 Tour KM

HDU3488 Tour [有向环覆盖 费用流]