HDU 4183 Pahom on Water (拆点最大流)

HDU 4183 Pahom on Water (拆点最大流)

tags: acm


做的时候和队友讨论了好久,总觉得最小生成树和最短路也有可能过,但最后还是选定了最大流.建图花了好久,然后发现交上去一直TLE,花了一个小时才查出来修改之前的从0到n-1的最大流代码的时候没有修改完全,导致一处循环判断条件出错一直循序,改正后AC.

题意:

给你一些平板的频率f,坐标(x,y),半径r,要求从f=400.0的点出发,经过有相交的平板,到达f=789.0的点,再回到f=400.0的点,要求除了起点外每个点最多只能访问一次,且到达f=789.0的点前只能从频率低的点到频率高的点,而回来的过程中只能从频率高的点到频率低的点.
问是否能够做到.

解析:

题目可转化为,在只存在从低到高的边的图上,起点(f=400.0)与终点(f=789.0)间是否存在两条不同的路径.

我们可以建立边容量为1且节点最大流量也为1的图,然后使用最大流求解源点和汇点之间的最大流量,流入汇点的流量即为从起点到终点不同的路径数.

边容量为1很容易做到,那节点最大流量要怎么控制呢?

这里就要用到一个建图技巧了.对于每一个顶点v,我们可以将其拆分为vi和vo两个顶点,所有指向v的边与vi相连,而所有由v出发的点由vo出发,同时vi与vo之间添加一条容量为k的边(此题容量为1),即可将通过顶点的容量限制为k以内.

最后输出最大流量即可.

如果使用的是Ford-Fulkerson的话还有一个小小的优化,因为每次标号-改进都代表一条独特的路径,最多只要增广两次就能得出答案,所以solve()函数中的循环只需要执行两次.

代码:

0 ms

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <queue>

using namespace std;

#define MAXN    605
#define INF        0x7f7f7f7f
#define MIN(a,b) ((a)<(b)?(a):(b))

struct NodeType
{
    double f;
    int x, y, r;
}Node[MAXN];

struct ArcType
{
    int c, f;
};

ArcType Edge[MAXN][MAXN];

int n, m;            //顶点数和弧数
int vis[MAXN];        //顶点状态:-1--未标号,0--已标号未检查,1--已标号已检查
int pre[MAXN];        //标号的第一个分量,标志当前顶点的标号由何处获得
int alpha[MAXN];    //标号的第二个分量,标志可改进量alpha

int s, t;

/*
*
* 默认s为源点,t为汇点
*
*/

void find_path()
{
    queue<int> Q;
    pre[s] = s; alpha[s] = INF; vis[s] = 1;
    Q.push(s);
    while (!Q.empty() && vis[t] == 0)
    {
        int u = Q.front();
        Q.pop();
        for (int i = 0; i < n; i++)
        {
            if (!vis[i])
            {
                if (Edge[u][i].c < INF && Edge[u][i].f < Edge[u][i].c)
                {
                    vis[i] = 1; pre[i] = u;
                    alpha[i] = MIN(alpha[u], Edge[u][i].c - Edge[u][i].f);
                    Q.push(i);
                }
                else if (Edge[i][u].c < INF && Edge[i][u].f >0)
                {
                    vis[i] = 1; pre[i] = -u;
                    alpha[i] = MIN(alpha[u], Edge[i][u].f);
                    Q.push(i);
                }
            }
        }
    }
}

void update_flow(int a)
{
    //根据可改进量修改流量
    int v1 = t;
    int v2 = abs(pre[v1]);
    while (v1 != s)
    {
        if (Edge[v2][v1].c < INF)
        {
            Edge[v2][v1].f += a;
        }
        else if (Edge[v1][v2].c < INF)
        {
            Edge[v1][v2].f -= a;
        }
        v1 = v2;
        v2 = abs(pre[v1]);
    }
}

int solve()
{
    int x = 2;
    while (x--)     //这个循环最多只需要2次
    {
        //初始化标志数组
        memset(vis, 0, sizeof(vis));
        memset(pre, 0, sizeof(pre));
        memset(alpha, 0, sizeof(alpha));
        find_path();
        if (vis[t] == 0 || alpha[t] == 0)
        {
            break;
        }
        update_flow(alpha[t]);
    }
    return Edge[s][s + n / 2].f;    //汇点内部流量即为不同路径条数
}

int main()
{
    int N;
    int T;
    scanf("%d", &T);
    while (T--)
    {
        s = -1;
        t = -1;
        scanf("%d", &N);
        memset(Edge, 0x7f, sizeof(Edge[0])*(N*2+1));
        n = N * 2;
        for (int i = 0; i < N * 2; i++)
        {
            Edge[i][i + N].c = 1;
            Edge[i][i + N].f = 0;
        }
        for (int i = 0; i < N; i++)
        {
            scanf("%lf %d %d %d", &Node[i].f, &Node[i].x, &Node[i].y, &Node[i].r);
            if (Node[i].f == 400.0)
                s = i;
            if (Node[i].f == 789.0)
                t = i;
        }
        Edge[s][s + N].c = N;
        Edge[s][s + N].f = 0;
        Edge[t][t + N].c = N;
        Edge[t][t + N].f = 0;

        for (int i = 0; i < N; i++)
        {
            for (int j = i + 1; j < N; j++)
            {
                if ((Node[i].r + Node[j].r)*(Node[i].r + Node[j].r) >= (Node[i].x - Node[j].x)*(Node[i].x - Node[j].x) + (Node[i].y - Node[j].y)*(Node[i].y - Node[j].y))
                {
                    if (Node[i].f < Node[j].f)
                    {
                        Edge[i + N][j].c = 1;
                        Edge[i + N][j].f = 0;
                    }
                    else if (Node[i].f > Node[j].f)
                    {
                        Edge[j + N][i].c = 1;
                        Edge[j + N][i].f = 0;
                    }

                }
            }
        }

        int ans = solve();
        if (ans >= 2)
            printf("Game is VALID\n");
        else
            printf("Game is NOT VALID\n");
        //printf("%d\n", ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值