题解——杀人游戏
图论建模-杀人游戏
杀人游戏
[中山市选]杀人游戏
题目描述一位冷血的杀手潜入Na-wiat,并假装成平民。警察希望能在
问:根据最优的情况,保证警察自身安全并知道谁是杀手的概率最大是多少?
输入格式
第一行有两个整数
接下来有
注:原文zz敏感内容已替换
输出格式
仅包含一行一个实数,保留小数点后面
样例 #1
样例输入 #1
5 4
1 2
1 3
1 4
1 5
样例输出 #1
0.800000
提示
警察只需要查证
对于
题意简述:给定一张有向图上有
如何想到图论建模呢,事实上,当我们面对类似于这种单向的二元关系问题,并且需要借助关系确定某些信息时,便可以想图论建模的思路
那么梳理一下思路,如果我们将每个人认识的人进行连一条有向边,记作
很明显,如果若干个点处于同一个
引理
一般情况下,确定每一个元素的颜色,最少的操作次数一定为零入度点的个数,
先证必要性:
显然,零入度点不可能被其他点所确定,故至少需要零入度点个数的操作次数才能覆盖整张图
再证充分性:
一个点
然后我们来思考有无特殊情况,考虑原图中的一个点
结合引理,我们得到了本题的算法流程
- 建图,执行缩点
- 对于缩点后的
,统计零入度点数量,记为 ,统计是零入度点,所在强连通分量大小为1并且满足其所连接的强连通分量的入度均大于1的 数量,记为 - 最终答案为:
对于本题需要注意的点是,在缩点建立新图的时候,很有可能出现重边的情况,大部分使用
注意我们的
- 清空
,这一步可以开一个 记录上一个强连通分量所连接的强连通分量进行撤销,保证复杂度 - 遍历第
个强连通分量的元素 ,对 执行操作3 - 遍历
的所有出边,在同一个强连通分量的不管,不在同一个强连通分量的,设后继点为 ,则若 未曾标记,将其入度加一并标记,若被标记,则不管
时间复杂度
本题总时间复杂度:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
#define N 150000
#define M 650000
int head[N],n,m,ans,p,num,ver[M],nxt[M],c[N],siz[N],in[N],cnt[N],tot,scc_cnt,vis[N],dfn[N],low[N],s[N],inn[N];
int shead[N],sver[M],snxt[M],stot;
void add_s(int u,int v){
snxt[++stot]=shead[u],sver[shead[u]=stot]=v;
}
vector<int>scc[N];
stack<int>t;
void add(int u,int v){
nxt[++tot]=head[u],ver[head[u]=tot]=v;
}
void tarjan(int u){
t.push(u);vis[u]=1;
dfn[u]=low[u]=++num;
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){
int v;
scc_cnt++;
do{
v=t.top();t.pop();
vis[v]=0;scc[scc_cnt].push_back(v);
c[v]=scc_cnt;siz[scc_cnt]++;
}while(u!=v);
}
}
bool check(int a){
for(int i=0;i<scc[a].size();i++){
int u=scc[a][i];
for(int i=head[u];i;i=nxt[i]){
int v=ver[i];
if(c[u]==c[v])continue;
if(in[c[v]]<2)return true;
}
}
return false;
}
void solve(){
if(n==1){
printf("1.000000\n");
return ;
};
for(int i=1;i<=scc_cnt;i++){
int num=0;
cnt[i]=1;
for(int j=0;j<siz[i];j++){
int u=scc[i][j];
for(int k=head[u];k;k=nxt[k]){
if(cnt[c[ver[k]]])continue;
in[c[ver[k]]]++;
cnt[c[ver[k]]]=1;
s[++num]=c[ver[k]];
}
}
cnt[i]=0;
while(num)cnt[s[num--]]=0;
}
int ans1=0,flag=1;
for(int i=1;i<=scc_cnt;i++){
if(in[i]==0){
ans1++;
}
}
if(ans1==1){
printf("%.6f\n",1.0-1.0/n);
return ;
}
for(int i=1;i<=scc_cnt;i++)if(in[i]==0&&!check(i)){ans1--;break;}
printf("%.6f\n",1.0*(n-ans1)/n);
return ;
}
void init(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
inn[v]++;
}
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
solve();
}
int main(){
init();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!