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

来源:互联网 发布:php源代码加密 原理 编辑:程序博客网 时间:2024/06/10 04:30

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];    //标号的第二个分量,标志可改进量alphaint 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;}
0 0
原创粉丝点击