解题报告 之 POJ1637 Sightseeing tour
Description
Input
Output
Sample Input
4 5 8 2 1 0 1 3 0 4 1 1 1 5 0 5 4 1 3 4 0 4 2 1 2 2 0 4 4 1 2 1 2 3 0 3 4 0 1 4 1 3 3 1 2 0 2 3 0 3 2 0 3 4 1 2 0 2 3 1 1 2 0 3 2 0
Sample Output
possible impossible impossible possible
题目大意:有n个城市,m条街道,可能是单向也可能是双向。问该图中存不存在从某一点出发的欧拉回路?
分析:首先明确一下欧拉回路的概念:从某点出发,每一条边都经过且只经过一次,包括双向边也只能走一次,而不是拆成两条边,所以不能直接当做单纯的有向边或者无向边的欧拉来做。故这种问题称为混合图的欧拉回路,用最大流来做。其实很明显这道题的难点在于怎么确定有向边到底用哪个方向?
如果有欧拉回路:无论有向边朝那个方向,有一点不会变,那就是某一个点的出边和入边只差是偶数,为什么呢?因为相当于在这条欧拉回路修改了一些有向边的方向。所以修改有向边时,一个节点出边+1,入边-1;而对应的节点入边+1,出边-1,但出入边只差从0变到+-2,即+-2n。所以最后一定是差为偶数。所以我们先跑一次欧拉判断,把不是偶数的情况直接输出impossible。
接下来处理都是偶数的情况。此时有一个巧思。如果一个节点入边大于出边,那么我们将这个差值/2看做这个点周围需要向外转向的条数。反之,如果出边大于入边,差值/2表示这个点需要向内转向的条数。那么就可以看做是流量负载了。对于所有入边大于出边的点,从超级源点拉一条边到该点,负载(in-out)/2,反之,从该点拉一条边到超级汇点,负载(out-in)/2。 对于所有无向边,我们从入点向出点拉一条负载为1的边(如果有k条重编则负载为k),意义是我们可以反向的资本。最后跑最大流判断是否等于总差值flow(跑欧拉检测是顺便统计)即可。
上代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN = 500;
const int MAXM = 4000;
const int INF = 0x3f3f3f3f;
struct Edge
{
int to, next, cap;
};
Edge edge[MAXM];
int level[MAXN];
int head[MAXN];
int ass[MAXN][MAXN];
int in[MAXN], out[MAXN];
int src, des, cnt,flow;
void addedge(int from,int to,int cap)
{
edge[cnt].to = to;
edge[cnt].cap=cap;
edge[cnt].next = head[from];
head[from] = cnt++;
edge[cnt].to = from;
edge[cnt].cap=0;
edge[cnt].next = head[to];
head[to] = cnt++;
}
int bfs()
{
queue<int> q;
while (!q.empty())
q.pop();
memset(level, -1, sizeof level);
level[src] = 0;
q.push(src);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (edge[i].cap > 0 && level[v] == -1)
{
level[v] = level[u] + 1;
q.push(v);
}
}
}
return level[des] != -1;
}
int dfs(int u, int f)
{
if (u == des) return f;
int tem;
for (int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if (edge[i].cap > 0 && level[v] == level[u] + 1)
{
tem = dfs(v, min(f, edge[i].cap));
if (tem > 0)
{
edge[i].cap -= tem;
edge[i ^ 1].cap += tem;
return tem;
}
}
}
level[u] = -1;
return 0;
}
bool Euler(int n)
{
flow = 0;
for (int i = 1; i <= n; i++)
{
if (in[i] - out[i] > 0) flow += (in[i] - out[i])/2;
if ((in[i] - out[i]) % 2 != 0)
return false;
}
return true;
}
int Dinic()
{
int ans = 0, tem;
while (bfs())
{
while (tem = dfs(src, INF))
{
ans += tem;
}
}
return ans;
}
int main()
{
int kase;
int n, m;
cin >> kase;
while (kase--)
{
memset(in, 0, sizeof in);
memset(out, 0, sizeof out);
memset(ass, 0, sizeof ass);
scanf("%d%d", &n, &m);
src = 0; des = n + 1;
for (int i = 1; i <= m; i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
out[a]++; in[b]++;
if (c == 0) ass[a][b]++;
}
if (!Euler(n))
{
cout << "impossible" << endl;
continue;
}
cnt = 0;
memset(head, -1, sizeof head);
for (int i = 1; i <= n; i++)
{
if (in[i] > out[i]) addedge(src, i, (in[i] - out[i])/2);
else if (out[i] > in[i]) addedge(i, des, (out[i] - in[i])/2);
for (int j = 1; j <= n; j++)
{
if (ass[i][j])
addedge(j, i, ass[i][j]);
}
}
if (Dinic() == flow)
cout << "possible" << endl;
else
cout << "impossible" << endl;
}
return 0;
}
今天心情不太好。怀疑自己是不是有天赋。我觉得还是自己水平不行,不然也不至于那么悲惨。可能是太浮躁了没有静下心来搞,也可能是什么都想搞,结果什么都搞不好。其实我更愿意相信是我的考试策略不对,但事实有摆在那里,容不得辩驳。罢了,不管中途结果如何也只能继续前行。A ship voyage's true paths never show before reaching the destination! 虽然没能去北京见偶像,但是还是衷心祝愿两位队友北京加油!!