POJ 1236 Tarjan缩点及思维..

  题意是有N个学校...每个学校可能向几个学校进行数据传输(单向的)...问..至少需要把一个文件给几个学校可以使给的N个学校都收到文件...再问在加几个通信线路可以使各个学校之间都能直接或间接的传递文件...

   一个强连通分量里的点肯定能互相传输...所以先用Tarjan缩点....如果一个点没有入度...那么则说明其他所有点无论如何都到不了这个点..如果要将文件传给所有点..那么首先没有入度的点必须要直接发文件...再想除了这些点..那么其他的点肯定有入度了...那么当把所有没有入度的点给选了之后..其他有入度的点必然能到达..所以第一问就是没有入度.点的个数..但要注意.如果都有入度..那么也是需要1个点的..因为至少要把文件给发下去吧..

   第二问....所有点都能互相到达...那么没有出度的点肯定要加出度边...没有入度的点肯定要加入度边...如果入度和出度点正好相等那么就两两配对加~~加的边正好就是没有入度或者说没有出度点的个数...如果没有出度边大于没有入度边..那么先两两配对..然后多的出度点再和一般的点来做边..那加的边正好是出度点的个数...反之如果没有入度的点多于没有出度的点..那么加的边数就是没有入度点的个数...所以第二问的答案就是没有入度的点和没有出度的点的最大值...

   一个要特别注意的..就是用Tarjan缩点后只剩一个点的情况..要特判输出 1  0


Program:

#include<iostream>
#include<math.h>
#include<stack>
#define MAXN 110
using namespace std;
struct p1
{
    int x,y,next;       
}line[MAXN*MAXN];
int n,k,m,i,A,B,link[MAXN],p,low[MAXN],dfn[MAXN],tp[MAXN],tpn;
bool init[MAXN],outit[MAXN],instack[MAXN];
stack<int> mystack;
void tarjan(int h)
{
     int k;
     instack[h]=true;
     dfn[h]=low[h]=++p;
     mystack.push(h);
     k=link[h];
     while (k)
     {
         if (!dfn[line[k].y])
         {
             tarjan(line[k].y);        
             low[h]=min(low[h],low[line[k].y]);
         }else
         if (instack[line[k].y])
         {
             low[h]=min(low[h],dfn[line[k].y]);                   
         }
         k=line[k].next;  
     }
     k=h;
     if (low[k]==dfn[k])
     {
          tpn++;              
          do
          {
              k=mystack.top();
              mystack.pop();
              tp[k]=tpn;             
          }         
          while (low[k]!=dfn[k]);                   
     }
     return;
}
void getanswer()
{
     int i;
     while (!mystack.empty()) mystack.pop(); 
     memset(dfn,0,sizeof(dfn));
     tpn=p=0;
     for (i=1;i<=n;i++)
     if (!dfn[i])
     {
          memset(instack,false,sizeof(instack));
          tarjan(i);           
     }
     memset(link,0,sizeof(link));     
     memset(init,false,sizeof(init));
     memset(outit,false,sizeof(outit));
     p=0;
     for (i=1;i<=m;i++)
     if (tp[line[i].x]!=tp[line[i].y])
     {
           p++;    
           line[p].x=tp[line[i].x];  line[p].y=tp[line[i].y];
           line[p].next=link[line[p].x];   
           link[line[p].x]=p;       
           outit[line[p].x]=true; init[line[p].y]=true;             
     }
     n=tpn;  m=p;
     //---------------------------------------------
     A=0;  B=0;
     for (i=1;i<=n;i++) 
     {
           if (!init[i]) A++;
           if (!outit[i]) B++;
     }
     B=A>B?A:B;         
     if (!A) A=1;     
     if (n==1) B=0;
     //---------------------------------------------
}
int main()
{ 
    while (~scanf("%d",&n))
    { 
         m=0;   
         memset(link,0,sizeof(link));
         for (i=1;i<=n;i++)
         while (~scanf("%d",&k))
         {
              if (!k) break; 
              m++;
              line[m].x=i; line[m].y=k;              
              line[m].next=link[i];  link[i]=m; 
         }  
         getanswer();
         printf("%d\n%d\n",A,B);
    } 
    return 0;
}


  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值