SPOJ IM 962 网络流最大流 解题报告

本文介绍了一个基于网络流算法解决特定路径寻找问题的方法。通过构建一个特殊的图模型,并使用Dinic算法来判断是否存在一条从起点到终点不重复经过任何节点的路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

M - Intergalactic Map

Map Jedi knights, Qui-Gon Jinn and his young apprentice Obi-Wan Kenobi, are entrusted by Queen Padmé Amidala to save Naboo from an invasion by the Trade Federation. They must leave Naboo immediately and go to Tatooine to pick up the proof of the Federation’s evil design. They then must proceed on to the Republic’s capital planet Coruscant to produce it in front of the Republic’s Senate. To help them in this endeavor, the queen’s captain provides them with an intergalactic map. This map shows connections between planets not yet blockaded by the Trade Federation. Any pair of planets has at most one connection between them, and all the connections are two-way. To avoid detection by enemy spies, the knights must embark on this adventure without visiting any planet more than once. Can you help them by determining if such a path exists?
Note - In the attached map, the desired path is shown in bold.

Input Description

The first line of the input is a positive integer t ≤ 20, which is the number of test cases. The descriptions of the test cases follow one after the other. The first line of each test case is a pair of positive integers n, m (separated by a single space). 2 ≤ n ≤ 30011 is the number of planets and m ≤ 50011 is the number of connections between planets. The planets are indexed with integers from 1 to n. The indices of Naboo, Tatooine and Coruscant are 1, 2, 3 respectively. The next m lines contain two integers each, giving pairs of planets that have a connection between them.

Output Description

The output should contain t lines. The ith line corresponds to the ith test case. The output for each test case should be YES if the required path exists and NO otherwise.

Example

Input

2
3 3
1 2
2 3
1 3
3 1
1 3

Output
YES
NO

【解题报告】

【题目大意】
在一个无向图中,一个人要从A点赶往B点,之后再赶往C点,且要求中途不能多次经过同一个点。问是否存在这样的路线。(3 <= N <= 30011, 1 <= M <= 50011)
【建模方法】
由于每个点只能走一次,似乎最短路之类的算法不能用,只有往网络流上靠。将每个点i拆成两个点i’, i’’并加边(i’, i’’, 1)就能轻易达到这个目的。起初我一直以A为源点思考,却怎么也想不出如何处理先后经过两个汇点的问题,直到灵光一现,想到可以以B为源点,A、C为汇点。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define N 30020
#define M 50020
#define inf 0x3f3f3f3f

int cnt,head[N],dis[N],vis[N];
struct Edge{int to,nxt,f;}e[M<<1];
int t,n,m,ss,tt;

void adde(int u,int v,int c)
{
    e[++cnt].to=v;e[cnt].f=c;
    e[cnt].nxt=head[u];head[u]=cnt;
    e[++cnt].to=u;e[cnt].f=0;
    e[cnt].nxt=head[v];head[v]=cnt;
}
bool bfs()
{
    memset(dis,0,sizeof(dis));
    memset(vis,0,sizeof(vis));
    queue <int> q;
    vis[ss]=1;q.push(ss);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];~i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(e[i].f&&!vis[v])
            {
                q.push(v);vis[v]=1;
                dis[v]=dis[u]+1;
            }
        }
    }
    return vis[tt];
}
int dfs(int u,int delta)
{
    if(u==tt) return delta;
    int ret=0;
    for(int i=head[u];delta&&~i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(e[i].f&&dis[v]==dis[u]+1)
        {
            int flow=dfs(v,min(e[i].f,delta));
            e[i].f-=flow;
            e[i^1].f+=flow;
            delta-=flow;
            ret+=flow;
        }
    }
    return ret;
}
int Dinic()
{
    int ret=0;
    while(bfs()) ret+=dfs(ss,inf);
    return ret;
}
int main()
{
    for(scanf("%d",&t);t;--t)
    {
        cnt=-1;
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        ss=0,tt=n*2+2;
        adde(ss,2+n,2);
        adde(1,tt,1);
        adde(3,tt,1);
        for(int i=4;i<=n;++i) adde(i,i+n,1);
        for(int i=1,u,v;i<=m;++i)  
        {  
            scanf("%d%d",&u,&v);  
            if(u<1||u>n||v<1||v>n) continue;  
            adde(u+n,v,1);adde(v+n,u,1);  
        }     
        puts(Dinic()==2?"YES":"NO");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值