tyvj间谍网络(强连通分量)题解

题目描述见下列网址

http://www.tyvj.cn/  (P1153)

经过对题目的分析,可以得出,题目的要求就是求出图中的强连通分量连,然后进行缩点操作,最后枚举每一个入度为0的点,如果可以有间谍的话,将贿赂所消耗的钱累加,否则的话记录下入度为0的点中最小点的标号。

分析:

  1.对强连通分量的求解使用TARJAN算发(因为数据量太大,3000,如果直接使用DFS的话为n^2+M,可能会超时)。

  2.在进行强连通分量缩点操作时,顺便记录缩成的点中贿赂间谍消耗的最小值,赋成这个点的权值。

  3.对缩成的点进行入度的处理,即循环每一个强连通分量中的点,如果同一个强连通分量的点之间存在边,则子节点的入度减1。

    最后枚举强连通分量中的点,只要存在一个点的入度大于0,则缩成的点的入度就大于0。

  4.最后就是用模拟链表存储边时,数组不可以开的太小。(本人就因为这个原因悲剧了两次)。

  5.ps.同样重要的是,C++中千万不要使用流读入,因为流的兼容性太强,速度奇慢,换标准输出(据统计标准读入是所有读入方法(readln,cin....)中

最快的)

  下面附上C++代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
struct
{
      int x,y,n;
}e[100001];
int f[3001];
int o;
bool v[3001];
bool vs[1001];
int b[3001][3001],dfn[3001],low[3001],zhan=0,lian,ti=0;
int stick[3001],money[3001];
int into[3001];
bool map[3001][3001];
int intot[3001];
int min(int a,int b)
{
    return (a<b)?a:b;
}
void add(int a,int b)
{
     o++;
     e[o].x=a;
     e[o].y=b;
     e[o].n=f[a];
     f[a]=o;
}
void tanzhan(int u)
{
     lian++;
     int j=1;
     while (stick[zhan]!=u)
     {
           b[lian][j++]=stick[zhan--];
           vs[stick[zhan]]=false;
     }
     b[lian][j++]=stick[zhan--];
     vs[u]=false;
     b[lian][0]=j-1;
}
void tarjan(int u)
{
     dfn[u]=++ti;
     if (low[u]==0) low[u]=dfn[u];
     stick[++zhan]=u;
     vs[u]=true;
     int t=f[u];
     v[u]=true;
     while (e[t].y!=0)
     {
           if (v[e[t].y]==false) 
           { 
                                tarjan(e[t].y);
                                low[u]=min(low[u],low[e[t].y]);
           }else
           {
                if (vs[e[t].y]==true)
                  low[u]=min(low[u],dfn[e[t].y]);
           }     
           t=e[t].n;           
      }
     if (dfn[u]==low[u]) tanzhan(u);    
}
int main()
{
    freopen("tarjan.in","r",stdin);
    freopen("tarjan.out","w",stdout);
    int n;
    memset(map,0,sizeof(map));
    scanf("%d",&n);
    int p;
    scanf("%d",&p);
    for (int i=1;i<=p;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if (money[a]==0||b<money[a])
        money[a]=b;
    }
    int r;scanf("%d",&r);
    for (int i=1;i<=r;i++)
    {
        int a,b;scanf("%d%d",&a,&b);
        add(a,b);
        into[b]++;
        map[a][b]=1;
    }
    for (int i=1;i<=n;i++)
    if (v[i]==false) tarjan(i);
    for (int i=1;i<=lian;i++)
    {
        intot[i]=0;
        for (int j=1;j<=b[i][0];j++)
        for (int k=1;k<=b[i][0];k++)
          if (k!=j)
          {
                   if (map[b[i][j]][b[i][k]]==1) into[b[i][k]]--;
          }
        for (int j=1;j<=b[i][0];j++)
          if (into[b[i][j]]>0) intot[i]=1;
    }
    int sum=0;
    int cant=100000000;
    for (int i=1;i<=lian;i++)
    {
        if (intot[i]==0)
        {
           int maxx=100000000;
           for (int j=1;j<=b[i][0];j++)
              if (money[b[i][j]]!=0&&money[b[i][j]]<maxx) maxx=money[b[i][j]];
           if (maxx==100000000)
           {  for (int j=1;j<=b[i][0];j++)
               if (b[i][j]<cant) cant=b[i][j];
           }else 
             if (maxx!=100000000) sum+=maxx;
        }
    }
    if (cant==100000000)
    {
                         printf("YES\n");
                         printf("%d\n",sum);
    }
    else
    {
          printf("NO\n");        
          printf("%d\n",cant);
     }
    return 0;
}
            
               
                  
              
      
        
      
          
        

    
    

 

本文地址:http://www.cnblogs.com/foreverzsz/archive/2010/11/07/1870968.html

(foreverzsz原创,转载请注明出处)

 

 

 

posted @ 2010-11-07 10:40  forever zsz  阅读(766)  评论(0编辑  收藏  举报