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;
}