圖論之殺人遊戲
阿新 • • 發佈:2020-07-17
題目
思路
對於一張圖來說,我們將其分為鏈(包括帶環鏈)和環
- 對於鏈,從鏈頂(入度為0)開始dfs記錄鏈的個數及大小,注意,大小為1的單點也包括在其中了;
- 處理完鏈後,對於單個的環來說,所有的點的入度都不為0,所以在處理完鏈之後還沒有處理的就是環了,再dfs一下就ok了;
顯然,對於每一個鏈狀結構(或者環狀結構)只需要減去一次鏈首(或環中任意一個點)是殺手的概率就可以了。
接下來來考慮單點情況 - 對於單點來說,如果將其他的點都查詢完了,最後剩下一個單點,那麼這個點是不需要查詢的;
- 但是在上面第一種情況下,每一個單點都被考慮了一次,實際對於n個單點,我們只需要考慮n-1次,所以對於存在單點的圖,我們把最後一個單點的情況加回來;
結束了???然而並沒有
對於上面情況,如果我們正向遍歷和反向遍歷結果是不一樣的
- 正向,圖中沒有單點,一條鏈和一個帶環鏈
- 反向,圖中有一個單點,一個帶環鏈,
所以我們需要正向反向都遍歷一遍,取最後概率最大值
程式碼
#include<bits/stdc++.h> using namespace std; const int maxn=1000000+10; const int maxm=300000; int head[maxn],ver[maxm],Next[maxm],tot,cnt; void add(int x,int y){ ver[++tot]=y,Next[tot]=head[x],head[x]=tot; } int siz[maxn]; int rd[maxn]; bool vis[maxn]; int n,m; void dfs(int x){//dfs求每個鏈狀結構或者環狀結構的大小 vis[x]=1; siz[cnt]++; for(int i=head[x];i;i=Next[i]){ if(!vis[ver[i]])dfs(ver[i]); } } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y); rd[y]++;//記錄入度 } int one=0;//記錄單點個數 double ans=1; for(int i=1;i<=n;i++){//正向遍歷處理鏈 if(!vis[i]&&rd[i]==0){ cnt++; dfs(i); ans-=(1.0/n);//每增加一個鏈狀結構,就相應減去鏈首為殺手的概率 if(siz[cnt]==1)one++; } } for(int i=1;i<=n;i++){//正向遍歷處理環 if(!vis[i]){ cnt++; dfs(i); ans-=(1.0/n); } } if(one)ans+=(1.0/n);//如果存在單點,把多減去的加上 /*====================接下來反向遍歷=========================*/ memset(siz,0,sizeof(siz)); memset(vis,0,sizeof(vis)); int one2=0;double ans2=1; for(int i=n;i>=1;i--){ if(!vis[i]&&rd[i]==0){ cnt++; dfs(i); ans2-=(1.0/n); if(siz[cnt]==1)one++; } } for(int i=n;i>=1;i--){ if(!vis[i]){ cnt++; dfs(i); ans2-=(1.0/n); } } if(one2)ans2+=(1.0/n); printf("%.6lf\n",max(ans,ans2));//取最大值 }