对峙
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; }