51nod 1299 监狱逃离

题意

监狱有N条道路连接N + 1个交点,编号0至N,整个监狱被这些道路连在一起(任何2点之间都有道路),人们通过道路在交点之间走来走去。其中的一些交点只有一条路连接,这些点是监狱的出口。在各个交点中有M个点住着犯人(M <= N + 1),剩下的点可以安排警卫,有警卫把守的地方犯人无法通过。给出整个监狱的道路情况,以及犯人所在的位置,问至少需要安排多少个警卫,才能保证没有1个犯人能够逃到出口,如果总有犯人能够逃出去,输出-1。

题解

首先这是一颗树
为了方便,我们选择一个不是出口的地方作为根
不存在的情况只有一个,那就是只有两个点,特判就可以了

然后考虑怎么构造答案
对于一个点x,他是有犯人的
并且往儿子走,会走到出口,那么我们必须把所有儿子封住
然后他就只能往上走了
对于一个节点,如果他不是有犯人,但是儿子会有犯人走到他这里,并且他的别的儿子有出口,那么就把它封住
容易知道,这样肯定是最优的
然后就没有了。。

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=100005;
int n,m;
struct qq
{
    int x,y,last;
}e[N*2];int num,last[N];
int d[N];
void init (int x,int y)
{
    num++;d[x]++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
int rt;
bool ok[N]; 
int g[N];//有多少个
int h[N];//有多少个
int ans=0;
void dfs (int x,int fa)
{
    bool ooo=false;
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        if (y==fa) continue;
        ooo=true;
        dfs(y,x);
        if (g[x]==1)//如果这个点本来就有,那么就GG了
        {
            if (h[y]!=0)
            {
                h[y]=0;
                ans++;
            }
        }
    }
    if (ooo==false)//叶子 
    {
    //  printf("%d %d\n",x,g[x]);
        if (g[x]==1) 
        {
            printf("-1");
            exit(0);
        }
        h[x]=1;
        return ;
    }
    if (g[x]==1)//堵死 
    {
        h[x]=0;
        return ;
    }
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        if (y==fa) continue;
        g[x]+=g[y];
        h[x]+=h[y];
    }
    if (g[x]!=0&&h[x]!=0)
    {
        ans++;
        g[x]=0;h[x]=0;
    }
}
int main()
{
    memset(ok,false,sizeof(ok));
    num=0;memset(last,-1,sizeof(last));
    scanf("%d%d",&n,&m);n++;
    if (n==2)
    {
        if (m!=0) printf("-1");
        else printf("0");
        return 0;
    }
    for (int u=1;u<n;u++)
    {
        int x,y;
        scanf("%d%d",&x,&y);x++;y++;
        init(x,y);init(y,x);
    }
    for (int u=1;u<=n;u++)
        if (d[u]!=1)
            {rt=u;break;}
        //  printf("%d\n",rt);
    for (int u=1;u<=m;u++)
    {
        int x;
        scanf("%d",&x);x++;
        g[x]=1;
    }
    dfs(rt,0);
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值