有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。
标签:ring 顺序 编程 turn 图论 size 技术 space ons
有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。
输入n人数<1000000 每个人的aim
你要求最后死亡数目的最小和最大可能
8
2 3 2 2 6 7 8 5
3 5
题解
这道题……说来话长了这就。考试的时候,一画样例明显是个图论题,出了环明显要缩点,缩了点明显还有一堆什么玩意,打着打着就忘了还有疯子自杀来着= =。不算自杀的,环里最多n-1最少n/2;链里用随便什么数据结构一个一个存,最大可能有入度的点都会死,这些东西还算好说。如果环又带了链,最小值怎么处理呢?当时没想清楚,直接用非常错误的取较大处理了,心里也觉得不对劲,但是没什么办法。
把图分成三种情况:自环一定死、独立环直接n-1和n/2处理、还有最小值需要一步步处理的。最大值除了独立的环会每环活下1个人,所有有入度的人都会死。自环输入时就把它标记为已死,最大最小都+1。考虑最小的情况,最后一种需要一个数据结构(栈或队列,我直接用的tarjan剩下的栈),每当一个点没有入度了就把它放进去,在栈里的人他的子节点是必死无疑了(敌人的安全就是自己的危险),他的孙节点入度-1,如果孙节点入度为0且没有死就把他也放进栈里。在栈里出现过或作为栈的子节点被杀死的点,它们的环都已经被拆开了,所以只有剩下的完整的环依然按照独立环去处理(改题的时候一直卡在这里)。有入度的环未必不独立,单纯看入度并不能判断最小杀死几个人。
做这道题的时候由于很多前面的数组不再有用,稍微废物利用了一下。但是一开始的bool型数组被来回来去用,导致自己的思路也很含混不清。数组尽量见名知意,确实有利于提高编程的准确程度。话说回来,这样计算死了几个人总有些草菅人命的感觉啊……
#include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<cmath> using namespace std; const int sj=1000010; int n,ai[sj],e,low[sj],c[sj],size[sj],a1,dfn[sj]; int a2,xt[sj],rh[sj],zd,zx,rd[sj]; stack<int> z; bool r[sj],zh[sj],ww[sj],ded[sj]; int bj(int x,int y) { return x<y?x:y; } void tarjan(int x) { low[x]=dfn[x]=++e; r[x]=1; z.push(x); if(!dfn[ai[x]]) { tarjan(ai[x]); low[x]=bj(low[x],low[ai[x]]); } else if(r[ai[x]]) low[x]=bj(low[x],dfn[ai[x]]); if(low[x]==dfn[x]) { a1++; do { a2=z.top(); z.pop(); r[a2]=0; c[a2]=a1; size[a1]++; }while(a2!=x); } } void init() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&ai[i]); if(ai[i]==i) { zx++,zd++; ded[i]=1; } rd[ai[i]]++; } for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); while(!z.empty()) z.pop(); for(int i=1;i<=n;i++) { if(c[i]!=c[ai[i]]) { xt[c[i]]=c[ai[i]]; rh[c[ai[i]]]++; } if(i==ai[i]) zh[c[i]]=1; } for(int i=1;i<=n;i++) if(!rd[i]) z.push(i); while(!z.empty()) { a2=z.top(); ww[c[a2]]=1; z.pop(); ww[c[ai[a2]]]=1; if(!ded[ai[a2]]) { zx++; ded[ai[a2]]=1; a2=ai[ai[a2]]; rd[a2]--; if(rd[a2]==0&&!ded[a2]) z.push(a2); } } for(int i=1;i<=a1;i++) { if(size[i]!=1&&!ww[i]) { if(size[i]&1) a2=(size[i]+1)>>1; else a2=size[i]>>1; zx+=a2; } if(size[i]!=1&&!rh[i]) zd+=size[i]-1; if(size[i]!=1&&rh[i]) zd+=size[i]; if(size[i]==1&&rh[i]&&!zh[i]) zd++; } printf("%d %d",zx,zd); } int main() { init(); return 0; }
谁终将声震人间,必长久深自缄默。谁终将点燃闪电,必长久如云漂泊。
标签:ring 顺序 编程 turn 图论 size 技术 space ons
原文地址:http://www.cnblogs.com/moyiii-/p/7275484.html