洛谷 P2712 摄像头 题解

题目大意:有一些摄像头,监视着一些位置,每个摄像头有自己的位置,如果这个摄像头的位置被其他摄像头监视着,那么它就不能被拆除,拆除一个摄像头之后就不能监视它原来监视的位置,问可否拆完全部摄像头。

这道题做法很多,这里提供两种做法,一种是普遍使用的做法,一种是我自己的做法(我的做法。。可能比较烦,但思路类似,请放心食用!)

做法1:

对于这道题,很容易可以想到,若摄像头A监视着摄像头B,那么A就向B连一条边,每一次只能拆除入度为0的点,也就是没有被监视着的摄像头,那么也就是拓扑排序,如果这个图有拓扑排序,那么一定可以拆完所有摄像头,如果没有,则输出最后剩下几个摄像头即可,这种大家普遍使用的做法这里就不细讲了,这题拓扑做法的题解很多,很容易找,时间复杂度为O(n+e)。

做法2:

这样做可能有点大材小用。。但是不要紧!条条道路通罗马嘛。

图就按上面的那样建,建完之后,我们首先考虑,什么样的摄像头拆不了?容易想到,在环里面的摄像头是拆不了的,因为你无法从里面任何一个摄像头入手,换个说法,在环里面每个点是可以走出去然后再走回来的,那么这就等价于环里面的每个摄像头都监视着自己,所以拆不了,那么就可以想到用强联通来搞,把图里面的全部强联通分量进行缩点,然后考虑缩点后的图,从每个入度为0的点出发,如果这个点不是一个环(这个点是一个环指的是它是自环或者它是缩成的点),那么ans++,然后继续遍历它连着的点即可,时间复杂度为O(n+e+n)。

代码如下:

#include <cstdio>
#include <cstring>

int n,len=0,lenn=0;
struct node{int x,y,next;};//邻接表 
node e[10010],ee[10010];
int first[110];
int a[510],shuru[100010],t=0;
bool huan[110];
void buildroad(int x,int y)
{
    len++;
    e[len].x=x;
    e[len].y=y;
    e[len].next=first[x];
    first[x]=len;
}
int ans,dfn[110],low[110],tot=0,zhan[110],belong[110],cnt=0,size[110];
bool v[110];
int minn(int x,int y){return x<y?x:y;}
void dfs(int x)
{
    if(x==0)return;
    zhan[++t]=x;//栈 
    v[x]=true;//标记是否在栈内 
    dfn[x]=low[x]=++tot;
    for(int i=first[x];i;i=e[i].next)
    {
        int y=e[i].y;
        if(dfn[y]==0)
        {
            dfs(y);
            if(low[y]<low[x])low[x]=low[y];
        }
        else if(v[y])low[x]=minn(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        int xx;cnt++;//cnt表示强联通分量个数 
        do
        {
            xx=zhan[t--];
            v[xx]=false;
            belong[xx]=cnt;//belong[i]表示i所在的强联通分量的编号 
            size[cnt]++;
        }while(xx!=x);
    }
}
int du[110];
void run(int x)
{
    if(!huan[x]&&du[x]==0)//假如x不是一个环,并且它的入度为零 
    {
        ans++;//拆除的摄像头数量+1 
        for(int i=first[x];i;i=ee[i].next)
        {
        	du[ee[i].y]--;//当x被摧毁,它所连向的点的入度都要-1 
			run(ee[i].y);
        }
    }
}

int main()
{
    scanf("%d",&n);ans=0;
    memset(a,-1,sizeof(a));//a[i]表示i位置的摄像头的编号,没有就是-1 
    for(int i=1;i<=n;i++)//摄像头按1~n编号,用数组shuru存下输入,为什么要这样处理仔细想想就明白了 
    {
        scanf("%d %d",&shuru[t+1],&shuru[t+2]);
        t+=2;
        a[shuru[t-1]]=i;//标记这个位置的摄像头的编号为i 
        int y=shuru[t];
        while(y--)scanf("%d",&shuru[++t]);
    }
    memset(first,0,sizeof(first));
    for(int i=0;i<t;)
    {
        int x=a[shuru[++i]],y=shuru[++i];
        while(y--)if(a[shuru[++i]]!=-1)buildroad(x,a[shuru[i]]);//假如摄像头x监视的这个位置有摄像头,就连一条边 
    }
    memset(dfn,0,sizeof(dfn));t=0;
    memset(v,false,sizeof(v));
    memset(size,0,sizeof(size));//size[i]表示i这个强联通分量的大小(包含多少个点) 
    for(int i=1;i<=n;i++)//强联通 
    if(dfn[i]==0)dfs(i);
    memset(first,0,sizeof(first));//记得清空,循环使用 
    memset(du,0,sizeof(du));//记录每个点(缩完后的途中)的入度 
    memset(huan,false,sizeof(huan));
    for(int i=1;i<=len;i++)
    {
        int x=e[i].x,y=e[i].y;
        du[belong[y]]++; 
        if(belong[x]==belong[y])huan[belong[x]]=true;//假如有一条边上的两个点处于同一个强联通分量,那么标记这个强联通分量为环 
        if(belong[x]!=belong[y])//假如不在同一个强联通分量,就建一条边 
        {
            ee[++lenn].x=belong[x];
            ee[lenn].y=belong[y];
            ee[lenn].next=first[belong[x]];
            first[belong[x]]=lenn;
        }
    }
    for(int i=1;i<=cnt;i++)
    if(!du[i])run(i);//假如没有入度,就从i开始遍历一遍 
    if(ans!=n)printf("%d",n-ans);
    else printf("YES");
}

往下看有福利





































其实之前我用一个WA的代码AC了这题,上面那个是改过的,详情请见这里

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值