POJ 1637 Sightseeing tour(混和图欧拉回路判断)

87 篇文章 0 订阅
20 篇文章 0 订阅
Sightseeing tour
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 9262 Accepted: 3914

Description

The city executive board in Lund wants to construct a sightseeing tour by bus in Lund, so that tourists can see every corner of the beautiful city. They want to construct the tour so that every street in the city is visited exactly once. The bus should also start and end at the same junction. As in any city, the streets are either one-way or two-way, traffic rules that must be obeyed by the tour bus. Help the executive board and determine if it's possible to construct a sightseeing tour under these constraints.

Input

On the first line of the input is a single positive integer n, telling the number of test scenarios to follow. Each scenario begins with a line containing two positive integers m and s, 1 <= m <= 200,1 <= s <= 1000 being the number of junctions and streets, respectively. The following s lines contain the streets. Each street is described with three integers, xi, yi, and di, 1 <= xi,yi <= m, 0 <= di <= 1, where xi and yi are the junctions connected by a street. If di=1, then the street is a one-way street (going from xi to yi), otherwise it's a two-way street. You may assume that there exists a junction from where all other junctions can be reached.

Output

For each scenario, output one line containing the text "possible" or "impossible", whether or not it's possible to construct a sightseeing tour.

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

Source


解题思路:

    非常好的一道题,自己没有想出来,参考了一下kuangbin的题解,不过感觉有的关于建图他好像有地方没有写清楚(也可能是我自己理解有问题),而且个人感觉他说的欧拉通路的扩展的解法中,随意确定的初始方向会造成在一些情况下即使存在欧拉通路也无法找到起点和点。

    首先,我们对图中每条无向边随意确定一个方向,然后统计每个顶点的入度出度。可以发现如果改变一条边的方向,那么与它相连的顶点的出度减入度一定变化2。所以如果存在初始出度入度差值为奇数的点,那么这个点的差值一定不可能通过边的反向变为0(也就是出度等于入度),那么就一定不能构成欧拉图。

    然后回想一下,网络流中增广路的增广过程就可以看作是把增广路上的边全部反向的过程,而且很容易发现对于增广路中间的结点,边反向的时候并没有改变它的差值。于是我们就可以考虑把差值作为流量,由于每次改变一定是2,所以我们应该把差值/2(为了方便就叫”cha值“了)作为流量。于是我们就可以开始建图了。

    N个顶点不变,再设置一个源点一个汇点,如果这个结点的cha值为正,我们就连一条从原点到这个结点容量为cha值的边,如果为负,就连接一条从此结点到汇点容量为-cha值的边。然后把原图中的每个已随意确定方向的无向边添加到图中容量为1。最后我们对这个图跑最大流,检查与源点连接的每条边是否全部满流,如果是就说明这些点的cha值已经变为0,则这个图可以形成欧拉图。(因为出度入度的变化一定是对称的,所以当初始cha值为正的点差值为0时,初始为负的点一定为0)

    这个问题还可以进行扩展:判断是否存在欧拉通路。只需要最开始的时候先判断一下,是否只有两个点的差值(不是cha值)为奇数,那么这两个一定又一个时起点一个是终点,就可以加一条连接着两条边的有向边(两种情况,都需要试一下),然后就转化成了刚才的欧拉图判读。

    最后附一组自己认为可能会使kuangbin的扩展问题解法出问题的数据(默认无向边初始定向时第一个输入的结点指向第二个输入的结点)。

4 3

1 2 0

1 3 0

2 4 0

3 4 0


AC代码:

#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstdio>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXV=200+3;

struct Edge
{
    int to,cap,rev;
    Edge(int t, int c, int r):to(t), cap(c), rev(r){}
};

int N,M;
int in[MAXV],out[MAXV];//每个节点的入度,出度
vector<Edge> G[MAXV];
int level[MAXV];
int iter[MAXV];

void add_edge(int from, int to, int cap)
{
    G[from].push_back(Edge(to, cap, G[to].size()));
    G[to].push_back(Edge(from, 0, G[from].size()-1));
}

void bfs(int s)
{
    mem(level,-1);
    queue<int> que;
    level[s]=0;
    que.push(s);
    while(!que.empty())
    {
        int v=que.front(); que.pop();
        for(int i=0;i<G[v].size();++i)
        {
            Edge &e=G[v][i];
            if(e.cap>0&&level[e.to]<0)
            {
                level[e.to]=level[v]+1;
                que.push(e.to);
            }
        }
    }
}

int dfs(int v, int t, int f)
{
    if(v==t)
        return f;
    for(int &i=iter[v];i<G[v].size();++i)
    {
        Edge &e=G[v][i];
        if(e.cap>0&&level[v]<level[e.to])
        {
            int d=dfs(e.to, t, min(f, e.cap));
            if(d>0)
            {
                e.cap-=d;
                G[e.to][e.rev].cap+=d;
                return d;
            }
        }
    }
    return 0;
}

int max_flow(int s, int t)
{
    int flow=0;
    for(;;)
    {
        bfs(s);
        if(level[t]<0)
            return flow;
        mem(iter,0);
        int f;
        while((f=dfs(s, t, INF))>0)
            flow+=f;
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&N,&M);
        // 0     s
        // 1 ~ N 节点
        // N+1   t
        int s=0,t=N+1;
        for(int i=0;i<=N+1;++i)
            G[i].clear();
        mem(in,0);
        mem(out,0);
        for(int i=0;i<M;++i)
        {
            int u,v,type;
            scanf("%d%d%d",&u,&v,&type);
            ++out[u];
            ++in[v];
            if(type==0)
                add_edge(u,v,1);
        }
        bool flag=true;
        for(int i=1;i<=N;++i)
        {
            int cha=out[i]-in[i];
            if(cha&1)//入度和出度永远不会相等
            {
                flag=false;
                break;
            }
            if(cha>0)
                add_edge(s,i,cha/2);
            else add_edge(i,t,-cha/2);
        }
        if(!flag)
        {
            puts("impossible");
            continue;
        }
        max_flow(s,t);
        for(int i=0;i<G[s].size();++i)
            if(G[s][i].cap!=0)//检查是否每个节点的入度都等于出度
            {
                flag=false;
                break;
            }
        puts(flag?"possible":"impossible");
    }
    
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值