Fzu 1719 Spy Network【最小点基----Tarjan强连通】

 Problem 1719 Spy Network

Accept: 74    Submit: 263
Time Limit: 1000 mSec    Memory Limit : 32768 KB

 Problem Description

Due to the large number of infiltration of foreign espionage, national security is at a high level of crisis. If Spy A grasps Spy B’s evidence of crimes, Spy A can exposed Spy B. Some spies take bribes, and if we give a number of dollars to them, they are willing to hand over all their evidences they have. Therefore, if we are able to buy some spies, we would control the whole spy network.
That is because once we arrest a spy, we will get all evidences he has. Then we can arrest new spies to acquire new information.
Now we know those who are willing to take dollars, and the number of dollars they need. We know what evidences have been got for each spy as well. Suppose that there are n (n <= 3000) spies in total, who are labeled as a number between 1 and n.If we can control all spies, output minimal dollars required, otherwise output each spies we cannot control.

 Input

The input consists of several test cases.For each case,the first line of input is an integer n. The second line is an integer p (1<=p<=n), which is the number of spies who accept dollars.Then following p lines, each of them contains two integers indicating the label of spies and the number he needs. An integer r (1 <= r <= 8000) followed, then r lines. Each contains two integers A and B, which means that Spy A has the evidence of Spy B.

 Output

For each case, If we can control all spies, output “YES” in the first line and the minimal number of dollar we need in the second line. If we cannot, output “NO” in the first line and the label of spy that we cannot control and have the minimal number of label.

 Sample Input

3
2
1 10
2 100
2
1 3
2 3
4
2
1 100
4 200
2
1 2
3 4

 Sample Output

YES
110
NO
3

 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();
    }
}








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值