欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > 信息学奥赛一本通 1517:间谍网络 | 洛谷 P1262 间谍网络

信息学奥赛一本通 1517:间谍网络 | 洛谷 P1262 间谍网络

2025/3/26 12:45:28 来源:https://blog.csdn.net/lq1990717/article/details/146437615  浏览:    关键词:信息学奥赛一本通 1517:间谍网络 | 洛谷 P1262 间谍网络

【题目链接】

ybt 1517:间谍网络
洛谷 P1262 间谍网络

【题目考点】

1. 图论:强连通分量 缩点

【解题思路】

每个间谍是一个顶点,如果间谍A可以揭发间谍B,那么从顶点A到顶点B有一条有向边,构成一个有向图。
一个间谍被控制,即被收买或揭发,在图论模型中对应为标记顶点。收买顶点的花费,称为标记顶点的代价
如果间谍A被控制,且A可以揭发B,那么间谍B也会被控制。在图论模型中就是如果标记了顶点A,且A到B有弧,那么标记顶点B。
现在存在一些初始被标记的顶点(初始被收买的间谍),问能否完成标记所有顶点。如果可以完成,输出收买间谍的最小花费。如果不能完成,输出编号最小的未标记顶点。
将每个强连通分量看作一个顶点,将原图g缩点会形成一个有向无环图sg。以下描述中,“某强连通分量”也指该强连通分量在缩点后形成的顶点。
一个强连通分量中的只要有一个顶点被标记,那么整个强连通分量中的顶点都会被标记,缩点后表示该强连通分量的顶点也会被标记。
遍历原图,对于每个顶点i, g C o s t i gCost_i gCosti表示标记顶点i的代价,顶点i所在强连通分量为 s c c i scc_i scci,使用 g C o s t i gCost_i gCosti更新标记 s c c i scc_i scci的最小代价 s g C o s t i sgCost_i sgCosti
如果一个顶点或强连通分量未被标记,则将 g C o s t i gCost_i gCosti s g C o s t i sgCost_i sgCosti设为0。
遍历所有强连通分量,关注入度为0的强连通分量:

  • 如果所有入度为0的强连通分量都已被标记,则所有顶点都可以被标记,标记顶点的最小代价为所有入度为0的强连通分量的最小标记代价 s g C o s t sgCost sgCost的加和。
  • 如果存在入度为0的强连通分量未被标记,则一定存在顶点未被标记。
    在缩点后的图上,从所有已标记的强连通分量出发进行深搜,把访问到的强连通分量都标记为已访问。
    再按顶点编号从小到大遍历所有顶点,如果顶点i所在的强连通分量是未访问的,则顶点i就是未被标记的编号最小的顶点。

【题解代码】

解法1:强连通分量 缩点

#include <bits/stdc++.h>
using namespace std;
#define N 3005
#define INF 0x3f3f3f3f
int n, scc[N], sn, degIn[N], dfn[N], low[N], ts, gCost[N], sgCost[N], sumCost;
vector<int> g[N], sg[N];//邻接表 g:原图  sg:缩点后的图 
bool inStk[N], allCtrled = true;
stack<int> stk;
bool vis[N];//vis[i]:间谍i能否被收买 
void tarjan(int u)
{int t;dfn[u] = low[u] = ++ts;stk.push(u);inStk[u] = true;for(int v : g[u]){if(dfn[v] == 0){tarjan(v);low[u] = min(low[u], low[v]);}else if(inStk[v])low[u] = min(low[u], dfn[v]);}if(dfn[u] == low[u]){sn++;do{t = stk.top();stk.pop();inStk[t] = false;scc[t] = sn;}while(t != u);}
}
void dfs(int u)
{vis[u] = true;for(int v : sg[u]) if(!vis[v])dfs(v);
}
int main()
{int p, r, a, b;cin >> n >> p;for(int i = 1; i <= p; ++i){cin >> a >> b;gCost[a] = b;//gCost[i]:如为0,i不能被收买。否则gCost[i]表示收买i的费用 }cin >> r;for(int i = 1; i <= r; ++i){cin >> a >> b;g[a].push_back(b);//a掌握b的证据,存在弧<a, b> }for(int i = 1; i <= n; ++i) if(dfn[i] == 0)//tarjan求强连通分量 tarjan(i);for(int u = 1; u <= n; ++u)//生成缩点后的图for(int v : g[u]) if(scc[u] != scc[v]){sg[scc[u]].push_back(scc[v]);degIn[scc[v]]++;//统计入度 }memset(sgCost, 0x3f, sizeof(sgCost));//sgCost[i]:强连通分量i中花费最小的间谍的花费,先把sgCost初始化为最大值for(int i = 1; i <= n; ++i) if(gCost[i] > 0)sgCost[scc[i]] = min(sgCost[scc[i]], gCost[i]);//更新i所在强连通分量中的最小花费 for(int i = 1; i <= sn ;++i) if(degIn[i] == 0){if(sgCost[i] == INF){ allCtrled = false;//allCtrled:是否可以控制所有间谍 break;}elsesumCost += sgCost[i];}if(allCtrled)cout << "YES" << endl << sumCost;else{for(int i = 1; i <= sn; ++i) if(sgCost[i] != INF && !vis[i])//只要强连通分量i中有被收买的间谍dfs(i); cout << "NO" << endl;for(int i = 1; i <= n; ++i) if(!vis[scc[i]])//只要当前i所属的强连通分量没有访问过(被收买),i就是没被收买的编号最小的间谍 {cout << i;break;}}return 0;
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词