对峙

Problem 2 对峙(kill.cpp/c/pas)

【题目描述】

有n名杀手,每人持有一把枪并对准了某名杀手(可以是自己)。杀手被枪杀后就不能再开枪了。

现在问你,在这种对峙情形下,被枪杀的杀手数量的最大值和最小值。开枪有严格的先后顺序,不存在同时开枪的情况。此外,不存在故意不开枪的情况,即每位杀手要么开枪,要么在开枪前被枪杀。

【输入格式】

第一行一个整数n,表示杀手人数。

第二行n个整数,第i个数表示第i名杀手的枪对准的杀手,编号从1到n。

【输出格式】

一行两个整数,表示死亡的杀手数的最大值和最小值。注意本题有部分分,最大值和最小值各5分,如果你无法得出某个值,请在对应位置输入任意整数以保证格式正确。

【样例输入】

4

2 3 1 1

【样例输出】

3 2

【数据范围】

对于30%的数据点,n <= 10;

对于60%的数据点,n <= 1000;

对于100%的数据点,n <= 100000。

【题目分析】

    假设环的长度为len

    最大:在一个环中最多死掉len-1个人,在链中最多也是死掉环的长度-1个人,最后只剩下入度为0的点活着(想想活着多么美好啊)

    最小:入度为0的点指向的点一定会死,然后删除这个死掉的点,把这个点指向其他点的边和其他点指向这个点的边都删除,然后继续找下一个点,每一次都把入度为0的点入队。在环中最少会有len/2个人死掉

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,ans1,ans2,len,bo,h;
int die[maxn],undie[maxn],q[maxn],a[maxn],In[maxn];

int main()
{
//    freopen("kill.in","r",stdin);
//    freopen("kill.out","w",stdout); 
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),
        In[a[i]]++;
    for(int i=1;i<=n;i++)
        if(In[i]==0)
            ans1++,
            q[++ans2]=i;
    for(int i=1;i<=ans2;i++)
    {
        int k=a[q[i]];//k表示入度为0的点能杀死的人
        if(die[k]) continue;
        //如果k还没有被杀死 
        die[k]=1;undie[a[k]]=1;
        --In[a[k]];
        if(In[a[k]]==0) q[++ans2]=a[k];
    }
    for(int i=1;i<=n;i++)
        if(In[i]&&!die[i])
        {
            len=bo=0;
            for(int j=i;!die[j];j=a[j])//找每个点,保证这个点没有被杀,照完之后找他可以杀掉的
                die[j]=1,//啊...死了 
                len++,//环的长度 
                bo+=undie[j];
            if(!bo&&len>1) //
                ans1++;
            ans2+=len/2; //在环内活着的人最多len/2个 
        }
    printf("%d %d\n",n-ans1,n-ans2);
//    fclose(stdin);fclose(stdout);
    return 0; 
}

 

posted @ 2016-10-21 21:20  [lemon]  阅读(225)  评论(0编辑  收藏  举报
……