Poj 1904 King's Quest 强连通分量

题目链接:

http://poj.org/problem?id=1904

题意:

有n个王子和n个公主,王子只能娶自己心仪的公主(一个王子可能会有多个心仪的公主),现已给出一个完美匹配,问每个王子都可以取哪些公主,并且保证取了一个公主后,全局还是存在完美匹配。

题解:

1、建图:

如果王子u对公主v心仪,则连一条边u->v。在样例给出的那组完美匹配中,如果王子u娶了公主v,连一条边v->u。

2、求强连通分量:

如果王子和自己心仪的公主属于同一个强连通分量,那么王子就可以娶这个公主。

  1 #include<iostream>
  2 #include<algorithm>
  3 #include<cstdio>
  4 #include<stack>
  5 #include<vector>
  6 #include<cstring>
  7 using namespace std;
  8 
  9 const int maxn=4444;
 10 
 11 int N;
 12 
 13 struct Edge{
 14     int v,ne;
 15     Edge(int v,int ne):v(v),ne(ne){}
 16     Edge(){}
 17 }egs[201010+maxn];
 18 
 19 int head[maxn],tot;
 20 
 21 void addEdge(int u,int v){
 22     egs[tot]=Edge(v,head[u]);
 23     head[u]=tot++;
 24 }
 25 
 26 int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
 27 stack<int> S;
 28 
 29 int scan(){
 30     int ret=0,flag=0; char ch;
 31     if((ch=getchar())=='-') flag=1;
 32     else if(ch>='0'&&ch<='9') ret=ch-'0';
 33     while((ch=getchar())>='0'&&ch<='9') ret=ret*10+ch-'0';
 34     return flag?-ret:ret;
 35 }
 36 
 37 void out(int x){
 38     if(x>9) out(x/10);
 39     putchar(x%10+'0');
 40 }
 41 
 42 void dfs(int u){
 43     pre[u]=lowlink[u]=++dfs_clock;
 44     S.push(u);
 45     for(int i=head[u];i!=-1;i=egs[i].ne){
 46         Edge& e=egs[i];
 47         int v=e.v;
 48         if(!pre[v]){
 49             dfs(v);
 50             lowlink[u]=min(lowlink[u],lowlink[v]);
 51         }else if(!sccno[v]){
 52             lowlink[u]=min(lowlink[u],pre[v]);
 53         }
 54     } 
 55     if(lowlink[u]==pre[u]){
 56         scc_cnt++;
 57         for(;;){
 58             int x=S.top(); S.pop();
 59             sccno[x]=scc_cnt;
 60             if(x==u) break; 
 61         }
 62     }
 63 }
 64 
 65 void find_scc(int n){
 66     dfs_clock=scc_cnt=0;
 67     memset(sccno,0,sizeof(sccno));
 68     memset(pre,0,sizeof(pre));
 69     for(int i=0;i<n;i++){
 70         if(!pre[i]) dfs(i);
 71     }
 72 }
 73 
 74 void build(){
 75     int cnt,v;
 76     for(int i=0;i<N;i++){
 77         cnt=scan();
 78         while(cnt--){
 79             v=scan(); v--;    
 80             addEdge(i,v+N);
 81         }
 82     }
 83     for(int i=0;i<N;i++){
 84         v=scan(); v--;
 85         addEdge(v+N,i);
 86     }
 87 }
 88 
 89 void init(){
 90     memset(head,-1,sizeof(head));
 91     tot=0;
 92 }
 93 
 94 int ans[201010],t;
 95 
 96 int main(){
 97     while(scanf("%d",&N)==1&&N){
 98         init();
 99         build();
100         find_scc(2*N);
101         for(int i=0;i<N;i++){
102             t=0;
103             for(int j=head[i];j!=-1;j=egs[j].ne){                
104                 Edge& e=egs[j];
105                 int v=e.v;
106                 if(sccno[i]==sccno[v]) ans[t++]=v;
107             }
108             sort(ans,ans+t);
109             out(t);
110             for(int i=0;i<t;i++){
111                 putchar(' ');
112                 out(ans[i]+1-N);
113             }
114             putchar('\n');
115         }
116     }
117     return 0;
118 } 

 

转载于:https://www.cnblogs.com/fenice/p/5509935.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值