题目:这里 题意:软件在安装之后需要重启才能发挥作用,现在给你一堆软件(有的需要重启有的不需要)以及安装这个软件之前需要哪些软件发挥作用,求最少的重启次数。 解法:真拓扑好题,但是我不会。然后这个题有两种解法,解法1来自斌神:普通的拓扑排序用了一个队列,而现在用两个队列q1,q2分别来存 不需要重启的software 和 需要重启的software。根据题目输入建好图后,按照拓扑序规则,首先将入度的0的点加进队列,当然不需要重启的进q1,需要的进q2。然后处理q1中的所有节点(即要删除这些点),那么要让与他们相连的节点的入度减1,如果发现减完入度为0,再判断其是否需要重启,并加进q1 or q2。一旦发现q1为空,那么使答案加1(即重启一次),把q2中所有元素加入q1,q2清空。如此循环直到q1,q2均为空即可。
//HDU 5098 topsort #include <bits/stdc++.h> using namespace std; const int maxn = 1020; map <string, int> mp; int cnt, edgecnt, reboot[maxn], out[maxn], in[maxn]; int head[maxn], vis[maxn]; struct edge{ int v, nxt; edge(){} edge(int v, int nxt) : v(v), nxt(nxt) {} }E[maxn*maxn]; void init(){ edgecnt = cnt = 0; mp.clear(); memset(head, -1, sizeof(head)); memset(reboot, 0, sizeof(reboot)); memset(out, 0, sizeof(out)); memset(in, 0, sizeof(in)); } void addedge(int u, int v){ E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++; } int topsort(){ queue <int> q1, q2; for(int i = 1; i <= cnt; i++){ if(in[i] == 0){ if(reboot[i] == 0) q1.push(i); else q2.push(i); } } int ans = 0; while(!q1.empty() || !q2.empty()){ if(q1.empty() && !q2.empty()){ ans++; while(!q2.empty()){ q1.push(q2.front()); q2.pop(); } } while(!q1.empty()){ int u = q1.front(); q1.pop(); for(int i = head[u]; ~i; i = E[i].nxt){ int v = E[i].v; in[v]--; if(in[v] == 0){ if(reboot[v] == 0) q1.push(v); else q2.push(v); } } } } return ans; } int main() { string s; char name[1010]; int T, ks = 0; scanf("%d", &T); getchar(); getchar(); while(T--){ init(); printf("Case %d: ", ++ks); while(getline(cin, s)){ if(s[0] == '\0') break; istringstream sin(s); sin >> name; int len = strlen(name); int flag = 0; if(name[len - 2] == '*'){ flag = 1; name[len - 2] = '\0'; } else{ name[len - 1] = '\0'; } string a, b; a = name; if(mp.find(a) == mp.end()) mp[a] = ++cnt; reboot[mp[a]] = flag; while(sin >> b){ if(mp.find(b) == mp.end()) mp[b] = ++cnt; addedge(mp[b], mp[a]); out[mp[b]]++; in[mp[a]]++; } } int ans = topsort(); printf("%d\n", ans); } return 0; }解法2来自hdu鸟神:建图同上,在建完的DAG图中求最长路,然后需要重启的点权为1,不需要的为0。这样找出最长路的权和即可。易证:只有具有从属关系的重启需要分别进行,不然可以同时搞掉。这样需要对每个入度为0的点求一次这样的最长路,取最大值即可,具体方法用DP可解。真orz。
//HDU 5098 topsort #include <bits/stdc++.h> using namespace std; const int maxn = 1020; map <string, int> mp; int cnt, edgecnt, reboot[maxn], out[maxn], in[maxn]; int head[maxn], vis[maxn], dp[maxn]; struct edge{ int v, nxt; edge(){} edge(int v, int nxt) : v(v), nxt(nxt) {} }E[maxn*maxn]; void init(){ edgecnt = cnt = 0; mp.clear(); memset(head, -1, sizeof(head)); memset(reboot, 0, sizeof(reboot)); memset(out, 0, sizeof(out)); memset(in, 0, sizeof(in)); } void addedge(int u, int v){ E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++; } int dfs(int u){ if(dp[u]) return dp[u]; int ans = 0; for(int i = head[u]; ~i; i = E[i].nxt){ int v = E[i].v; ans = max(ans, dfs(v)); } return dp[u] = ans + reboot[u]; } int DAGdp(){ int ans = 0; memset(dp, 0, sizeof(dp)); for(int i = 1; i <= cnt; i++){ if(in[i] == 0){ ans = max(ans, dfs(i)); } } return ans; } int main() { string s; char name[1010]; int T, ks = 0; scanf("%d", &T); getchar(); getchar(); while(T--){ init(); printf("Case %d: ", ++ks); while(getline(cin, s)){ if(s[0] == '\0') break; istringstream sin(s); sin >> name; int len = strlen(name); int flag = 0; if(name[len - 2] == '*'){ flag = 1; name[len - 2] = '\0'; } else{ name[len - 1] = '\0'; } string a, b; a = name; if(mp.find(a) == mp.end()) mp[a] = ++cnt; reboot[mp[a]] = flag; while(sin >> b){ if(mp.find(b) == mp.end()) mp[b] = ++cnt; addedge(mp[b], mp[a]); out[mp[b]]++; in[mp[a]]++; } } int ans = DAGdp(); printf("%d\n", ans); } return 0; }