题目意思:有一批软件需要安装,“:”前的软件需要在“:”后的软件安装完成后才能安装(“:”后面可能有多个软件),“*”表明该软件需要重启才完成安装。
求所有软件都完成安装最少重启的次数。
解题思路:
双队列top排序,用两个队列q1,q2分别来存不需要重启的软件和需要重启的软件。首先将入度的0的点加进队列,当然不需要重启的进q1,需要重启的进q2。然后删除q1中的所有节点,让与他们相连的节点的入度减1,如果发现减完入度为0,再判断其是否需要重启,并加进q1 or q2。一旦发现q1为空,那么使答案加1(即重启一次),把q2中所有元素加入q1,q2清空。如此循环直到q1,q2均为空即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<vector> #include<queue> #include<set> #include<map> #include<cmath> #define INF 0xfffffff #define MAX 1000000 #define CLR(a,b) memset((a),(b),sizeof(a) ) using namespace std; typedef long long ll; int edge[2005][2005]; int vis[2005]; int degree[2005]; int tot,ans; map<string,int>mp; queue<int>q1,q2; void top_sort(){ for(int i=1;i<=tot;i++) if(!degree[i]){ if(!vis[i]) q1.push(i); else q2.push(i); } while(!q1.empty()||!q2.empty()){ if(q1.empty()&&!q2.empty()){ ans++; while(!q2.empty()){ int x=q2.front(); q2.pop(); q1.push(x); } } while(!q1.empty()){ int x=q1.front(); q1.pop(); for(int i=1;i<=tot;i++) if(edge[i][x]){ degree[i]--; if(!degree[i]){ if(!vis[i]) q1.push(i); else q2.push(i); } } } } } void init(){ CLR(degree,0); CLR(edge,0); CLR(vis,0); tot=0; ans=0; mp.clear(); while(!q1.empty()) q1.pop(); while(!q2.empty()) q2.pop(); } string p,last[1005]; int main() { int a,b; int t; string s; scanf("%d",&t); getchar(); getchar(); for(int cas=1;cas<=t;cas++){ init(); while(getline(cin,s)!=NULL){ if(s[0]=='\0') break; int len=s.size(); int flag=0; int havelast=0; p=""; int lastnum=-1; for(int i=0;i<len;i++){ if(s[i]==' '){ last[++lastnum]=""; continue; } if(s[i]=='*'){ flag=1; continue; } if(s[i]==':'){ havelast=1; continue; } if(havelast==0) p+=s[i]; else last[lastnum]+=s[i]; } if(mp.find(p)==mp.end()) mp[p]=++tot; vis[mp[p]]=flag; int m=0; while(last[m].size() > 0 && m <= lastnum){ if(mp.find(last[m])==mp.end()) mp[last[m]]=++tot; edge[mp[p]][mp[last[m]]]=1; m++; } } for(int i=1;i<=tot;i++) for(int j=1;j<=tot;j++) if(edge[i][j]) degree[i]++; top_sort(); cout<<"Case "<<cas<<": "<<ans<<endl; } return 0; }
时间: 2024-10-13 23:28:30