洛谷P2321 [HNOI2006]潘多拉的宝盒

 

 

 

 极其恶心的题目描述,题都读不懂

题意(大概版,详细的还要读题):有几个信号大元,输出元内有几个元件,每个元件如果有信号就有两条途径,它既可以把他的信号传给一个元件并在信号之后加一个0,也可以传给另一个元件并在其后加一个1,只到碰见大元内的输出元,这样大元就会输出此时的信号,如果一个大元可以输出另一个大元可以输出的所有信号,那么我们就称这个大元是另一个大元的升级,最后要求出所有大元内最长的连续升级序列的长度。

  这道题在读懂题之后应该不难想到我们应该将所有的大元用升级关系连成一个图,之后用tanjan(读作tayang!!!)缩点,因为如果两个大元相等的话他们就是彼此的升级,就会出现环的情况,最后跑一遍最短路即可。

  现在的问题是我们应该如何判断两个大元间的升级关系呢?我们想到了BFS。

  我们可以记录每个大元内的输出元,在判断关系时从两个大元内任找一个起点,之后走相同的0,1路径,如果其中一个走到了输出元而另一个却没有,我们就可以判定他们之间一定不是升级关系。如果bfs结束依然符合情况他们就有升级关系,由于数据小,用邻接矩阵存图即可,在加边时可以用到一个小技巧,如果它的反向边已经加过(即两个大元相等)就不要再加了,这样就可以省去tarjan(当然有大佬想写我也不拦着),之后直接Floyd求出最长路即可。

  可能有人会问只加单向边会不会影响答案,是不会的,既然两个大元已经相等了,这两个大元所连的边就都是一样的,可以看做等效,不用担心影响答案。

上代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<map>
 5 #include<queue>
 6 using namespace std;
 7 const int N=55;
 8 typedef long long ll;
 9 struct Node{ //一个大元 
10     int n,m;
11     int path[N][2];//path记录点的不同路径 
12     int out[N];//记录大元内出度 
13 }a[N];
14 int c[N][N];//存图 
15 struct E{
16     int next,to,dis;
17 }edge[N];
18 int Head[N*4],tot;
19 void Add(int x,int y,int z){
20     edge[++tot].next=Head[x];
21     edge[tot].to=y;
22     edge[tot].dis=z;
23     Head[x]=tot;
24 }
25 bool bfs(Node a,Node b){
26     queue<pair<int,int> >q; //bfs,判断关系 a是不是b的升级 
27     int vis[N][N];
28     memset(vis,0,sizeof(vis));
29     q.push(make_pair(0,0));
30     while(!q.empty()){
31         pair<int,int>top=q.front();q.pop();
32         if(a.out[top.first]&&!b.out[top.second]) //不是升级条件(由于只单方面判断a是不是b的升级不用写 
33             return false;                        //a未出而b出) 
34         for(int i=0;i<=1;++i){
35             int x=a.path[top.first][i];
36             int y=b.path[top.second][i];
37             if(vis[x][y]) continue;//遍历过 
38             vis[x][y]=1;
39             q.push(make_pair(x,y));
40         }
41     }
42     return true; //全部符合是升级 
43 }
44 int main(){
45     int T;
46     scanf("%d",&T);
47     for(int i=1;i<=T;++i){//繁琐的输入 
48         int x,y,t;
49         scanf("%d%d",&x,&y);
50         a[i].n=x;a[i].m=y;
51         for(int j=1;j<=y;++j){
52             scanf("%d",&t);
53             a[i].out[t]=1;
54         }
55         for(int j=0;j<x;++j){
56             int p,q;
57             scanf("%d%d",&p,&q);
58             a[i].path[j][0]=p;a[i].path[j][1]=q;
59         }
60     }
61     for(int i=1;i<=T;++i)
62     for(int j=1;j<=T;++j)
63         c[i][j]=-0x3f3f3f3f;
64     for(int i=1;i<=T;++i)
65     for(int j=1;j<=T;++j)
66         if(i!=j&&bfs(a[i],a[j])&&c[j][i]<0)//加边同时排除相等情况 
67             c[i][j]=1;
68     int ans=-1;
69     for(int k=1;k<=T;++k) //Floyd 
70     for(int i=1;i<=T;++i)
71     for(int j=1;j<=T;++j)
72         if(c[i][j]<c[i][k]+c[k][j]&&c[i][k]>0&&c[k][j]>0)
73             c[i][j]=c[i][k]+c[k][j],ans=max(ans,c[i][j]);
74     printf("%d\n",ans+1);//算上起点+1 
75     return 0;
76 }
View Code

猜你喜欢

转载自www.cnblogs.com/li-jia-hao/p/12808258.html
今日推荐