Problem 1719 Spy NetworkAccept: 74 Submit: 263
Time Limit: 1000 mSec Memory Limit : 32768 KB
Problem Description
Input
Output
Sample Input
Sample Output
Source
FZU 2009 Summer Training Qualification -- Hero Revival 1
有n个间谍,其中有p个人可以被贿赂(贿赂的人的信息也会自动给出),有r条有向边,表示编号为a的人可以提供出编号为b的人的信息。(如果a能够提供b的信息,b也能提供c的信息,那么a也能提供c的信息)问如果能够通过贿赂人就能将这n个人的信息都买到,那么输出YES和最小花费。否则输出NO以及不能被控制的人的最小编号。
思路:
1、简单分析问题,如果图中有有向环的话,我们可以将这一个有向环看成一个点,因为环内任意一个人都能提供出这个环内所有人的信息,所以我们使用Tarjan/Kosaraju之类的强连通算法缩点染色,使得图变成一个DAG(有向无环)图。
2、如果当前图变成了一个DAG图,那么度为0的节点一定是需要贿赂的人,因为这个节点中的人的信息不可能通过别人来获得,只能通过直接贿赂获得其信息,所以无论输出YES还是NO,我们都将问题锁定在度为0的关键节点上来讨论,也就是所谓的最小点基问题。
3、这个时候再经过简单分析,如果所有度为0的节点都能通过直接贿赂来得到其信息,那么其整个图的信息也就全部得到了,这个时候输出YES,并且在每个节点中找到最小花费点,贿赂这个人即可。
4、如果存在度为0的节点不能通过直接贿赂来得到其信息,那么整个图的信息就是残缺不全的,那么这个时候输出NO。但是相关这个最小编号问题,可能是出题人根据其他某个题修改成这么个题,题干中说的是要输出每一个不能控制的间谍,但是在output要求的地方,又要求只输出编号最小节点,但是通过测试,这个最小编号节点又是度为0的节点,比如这样一个样例:
4
1
3 100
3
2 1
1 3
3 4
Ac代码输出:
No
2
但是1明明才是最小不能控制的编号啊!不知道是题主想的简单了还是我想的多了,或者是英文翻译的不准确之类的问题,总之,输出No的时候,题目要求输出的是度为0的节点中编号最小的顶点编号。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
int head[100000];
struct EdgeNode
{
int to;
int w;
int next;
}e[1000000];
int n,cont,sig,cnt,tt;
vector<int >mp[3500];
int degree[3500];
int stack[3500];
int color[3500];
int vis[3500];
int low[3500];
int dfn[3500];
int need[3500];
void add(int from,int to)
{
e[cont].to=to;
e[cont].next=head[from];
head[from]=cont++;
}
void Tarjan(int u)
{
vis[u]=1;
low[u]=dfn[u]=cnt++;
stack[++tt]=u;
for(int i=head[u];i!=-1;i=e[i].next)
{
int v=e[i].to;
if(vis[v]==0)Tarjan(v);
if(vis[v]==1)low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
sig++;
do
{
color[stack[tt]]=sig;
vis[stack[tt]]=-1;
}
while(stack[tt--]!=u);
}
}
void Slove()
{
cnt=1;
sig=0;
tt=-1;
memset(dfn,0,sizeof(dfn));
memset(color,0,sizeof(color));
memset(vis,0,sizeof(vis));
memset(stack,0,sizeof(stack));
memset(low,0,sizeof(low));
memset(degree,0,sizeof(degree));
for(int i=1;i<=n;i++)mp[i].clear();
for(int i=1;i<=n;i++)
{
if(vis[i]==0)Tarjan(i);
}
//缩点染色之后的图中度为0的节点如果能够被贿赂就是Yes,否则就是No
for(int i=1;i<=n;i++)
{
for(int j=head[i];j!=-1;j=e[j].next)
{
int u=i,v=e[j].to;
if(color[u]!=color[v])
{
mp[color[u]].push_back(color[v]);
degree[color[v]]++;
}
}
}
memset(vis,0,sizeof(vis));
//判断一下哪些点可以被贿赂。
for(int i=1;i<=n;i++)
{
if(need[i]==-1)continue;
vis[color[i]]=1;
}
int flag=0;
for(int i=1;i<=sig;i++)
{
if(degree[i]==0&&vis[i]==0)flag=1;
}
if(flag==1)
{
printf("NO\n");
for(int i=1;i<=n;i++)
{
if(degree[color[i]]==0&&vis[color[i]]==0)
{
printf("%d\n",i);
break;
}
}
}
else
{
printf("YES\n");
int output=0;
for(int i=1;i<=sig;i++)
{
if(degree[i]==0)
{
int sum=0x3f3f3f3f;
for(int j=1;j<=n;j++)
{
if(color[j]==i)
{
if(need[j]!=-1)
sum=min(sum,need[j]);
}
}
output+=sum;
}
}
printf("%d\n",output);
}
}
int main()
{
while(~scanf("%d",&n))
{
cont=0;
memset(head,-1,sizeof(head));
memset(need,-1,sizeof(need));
int p;
scanf("%d",&p);
while(p--)
{
int num,val;
scanf("%d%d",&num,&val);
need[num]=val;
}
scanf("%d",&p);
while(p--)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
}
Slove();
}
}
699

被折叠的 条评论
为什么被折叠?



