P1211 街道赛跑

  又是一下午的杠题,累啊~~~

  这道题第一问很简单,只需去掉一个点,判断能不能到达终点就行了;

  第二问其实仔细想想也不难,就是判断去掉一个点后是否形成两个图;首先要知道是建立在第一问的基础上的;在加边的同时预处理一个e2的双向图,分别列举第一问的点,将从0到这个点所经过的点做标记;再将这点到终点遍历一遍,看是否遇到已标记过的点就行了。

  一个160行的代码,自己看吧...

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
    int x = 0,ff = 1;char ch = getchar();
    while(!isdigit(ch))
    {
        if(ch == '-') ff = -1;
        ch = getchar();
    }
    while(isdigit(ch))
    {
        x = (x<<1) + (x<<3) + (ch ^ 48);
        ch = getchar();
    }
    return x * ff;
}

inline void write(int x)
{
    if(x < 0) putchar('-'),x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

struct node
{
    int y,next;
}e[1231111],e2[123211];
int a,tot = 0,head,tail,lin[1231121];
int tot2 = 0,lin2[1231221],vis2[1231213];
int q[12311121],vis[1231112],dis[1231111];
int f[123211],top = 0,F[132121],tom = 0;

void add(int xx,int yy)
{
    e[++tot].y = yy;
    e[tot].next = lin[xx];
    lin[xx] = tot;
}

void add2(int xx,int yy)
{
    e2[++tot2].y = yy;
    e2[tot2].next = lin2[xx];
    lin2[xx] = tot2;
}

void Init()
{
    int flag = 0;
    for(int i = 0;;++i)
    {
        int x;
        for(;;)
        {
            x = read();
            if(x == -2) break;
            if(x == -1) 
            {
                flag = 1;
                break;
            }
            add(i,x);
            add2(i,x);
            add2(x,i);
        }
        if(flag == 1) 
        {
            a = i - 1;
            break;
        }
    }
}

void check(int m)
{
    memset(vis,false,sizeof(vis));
    vis[0] = true; vis[m] = true;
    head = 1,tail = 1;
    q[1] = 0;
    for(head = 1;head <= tail;++head)
    {
        int x = q[head];
        for(int i = lin[x],y;i;i = e[i].next)
        {
            if(!vis[y = e[i].y])
            {
                vis[y] = true;
                q[++tail] = y;
            }
        }
    }
}

bool check2(int m)
{
    memset(vis,false,sizeof(vis));
    memset(vis2,false,sizeof(vis2));
    vis[0] = true; vis[m] = true;
    head = 1,tail = 1;
    q[1] = 0;
    for(head = 1;head <= tail;++head)
    {
        int x = q[head];
        for(int i = lin2[x],y;i;i = e2[i].next)
        {
            if(!vis[y = e2[i].y])
            {
                vis[y] = true;
                q[++tail] = y;
            }
        }
    }
    
    head = 1,tail = 1;
    q[1] = m; vis2[m] = true; vis[m] = false;
    for(head = 1;head <= tail;++head)
    {
        int x = q[head];
        for(int i = lin[x],y;i;i = e[i].next)
        {
            if(vis[y = e[i].y]) return false;
            if(!vis2[y])
            {
                vis2[y] = true;
                q[++tail] = y;
            }
        }
    }    
    return true;
}

int main()
{
    Init();
    for(int i = 1;i < a;++i)
    {
        check(i);
        if(!vis[a]) f[++top] = i;
    }
    write(top); putchar(' ');    
    for(int i = 1;i <= top;++i)
    {
        write(f[i]);
        putchar(' ');
    }
    putchar('\n');
    for(int i = 1;i <= top;++i)
    {
        if(check2(f[i])) F[++tom] = f[i];
    } 
    write(tom); putchar(' ');
    for(int i = 1;i <= tom;++i)
    {
        write(F[i]);
        putchar(' ');
    }
    return 0;
}

 

  

 

转载于:https://www.cnblogs.com/AK-ls/p/10164738.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值