您的位置:首页 > 其它

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

2015-08-24 16:03 381 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: