Hdu--5098(拓扑排序 or DP,贪心)
2014-11-02 19:20:31
思路:好题!这道题在邀请赛的时候想了1小时,只想到肯定是拓扑序,没有深究。回来请教了bin神和YYN,分别得到的两种思路:
bin神:普通的拓扑排序用了一个队列,而现在用两个队列q1,q2分别来存 不需要重启的software 和 需要重启的software。根据题目输入建好图后,按照拓扑序规则,首先将入度的0的点加进队列,当然不需要重启的进q1,需要的进q2。然后处理q1中的所有节点(即要删除这些点),那么要让与他们相连的节点的入度减1,如果发现减完入度为0,再判断其是否需要重启,并加进q1 or q2。一旦发现q1为空,那么使答案加1(即重启一次),把q2中所有元素加入q1,q2清空。如此循环直到q1,q2均为空即可。
YYN:建图同上,在建完的DAG图中求最长路,然后需要重启的点权为1,不需要的为0。这样找出最长路的权和即可。易证:只有具有从属关系的重启需要分别进行,不然可以同时搞掉。这样需要对每个入度为0的点求一次这样的最长路,取最大值即可,具体方法用DP可解。
(1)双队列版:(QAQ,一开始WA到死,后来问了bin神才知道ver[maxn * maxn]开成了ver[maxn],简直被自己蠢哭了,以后坚决不犯这样的低级错误,检查代码一定要仔细!)
1 /************************************************************************* 2 > File Name: i.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Sun 02 Nov 2014 01:36:00 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <sstream> 16 #include <string> 17 #include <stack> 18 #include <queue> 19 #include <iostream> 20 #include <algorithm> 21 using namespace std; 22 #define lp (p << 1) 23 #define rp (p << 1|1) 24 #define getmid(l,r) (l + (r - l) / 2) 25 #define MP(a,b) make_pair(a,b) 26 typedef long long ll; 27 const int INF = 1 << 30; 28 const int maxn = 1010; 29 30 int n,m,cnt; 31 int din[maxn],dout[maxn],reb[maxn]; 32 int vis[maxn],first[maxn],next[maxn * maxn],ver[maxn * maxn],ecnt; 33 string s,id1,id2; 34 char name[1500]; 35 map< string,int > mp; 36 queue<int> q1,q2; 37 38 void Add_edge(int u,int v){ 39 next[++ecnt] = first[u]; 40 ver[ecnt] = v; 41 first[u] = ecnt; 42 } 43 44 void Init(){ 45 memset(reb,0,sizeof(reb)); 46 memset(vis,0,sizeof(vis)); 47 memset(din,0,sizeof(din)); 48 memset(dout,0,sizeof(dout)); 49 memset(first,-1,sizeof(first)); 50 while(!q1.empty()) q1.pop(); 51 while(!q2.empty()) q2.pop(); 52 ecnt = cnt = 0; 53 mp.clear(); 54 } 55 56 int Topo_count(){ 57 for(int i = 1; i <= cnt; ++i) if(din[i] == 0){ 58 if(reb[i] == 0) q1.push(i); 59 else q2.push(i); 60 } 61 int res = 0; 62 while(!q1.empty() || !q2.empty()){ 63 if(q1.empty() && !q2.empty()){ 64 ++res; 65 while(!q2.empty()){ 66 int x = q2.front(); q2.pop(); 67 q1.push(x); 68 } 69 } 70 while(!q1.empty()){ 71 int x = q1.front(); q1.pop(); 72 vis[x] = 1; 73 for(int i = first[x]; i != -1; i = next[i]){ 74 int v = ver[i]; 75 din[v]--; 76 if(din[v] == 0){ 77 if(!reb[v]) q1.push(v); 78 else q2.push(v); 79 } 80 } 81 } 82 } 83 return res; 84 } 85 86 int main(){ 87 int a,b,len,flag; 88 scanf("%d",&n); 89 getchar(); 90 getchar(); 91 for(int Case = 1; Case <= n; ++Case){ 92 Init(); 93 while(getline(cin,s) != NULL){ 94 if(s[0] == '\0') 95 break; 96 istringstream sin(s); 97 sin >> name; 98 flag = 0; 99 int len = strlen(name); 100 for(int i = 0; i < len; ++i) 101 if(name[i] == '*') flag = 1; 102 name[len - 1 - flag] = '\0'; 103 id1 = name; 104 if(mp.find(id1) == mp.end()) 105 mp[id1]= ++cnt; 106 reb[mp[id1]] = flag; 107 while(sin >> id2){ 108 if(mp.find(id2) == mp.end()) 109 mp[id2] = ++cnt; 110 Add_edge(mp[id2],mp[id1]); 111 dout[mp[id2]]++; 112 din[mp[id1]]++; 113 } 114 } 115 printf("Case %d: %d\n",Case,Topo_count()); 116 } 117 return 0; 118 }
(2)DP版:裸的记忆化搜索找DAG最长路
1 /************************************************************************* 2 > File Name: i.cpp 3 > Author: Nature 4 > Mail: 564374850@qq.com 5 > Created Time: Sun 02 Nov 2014 01:36:00 PM CST 6 ************************************************************************/ 7 8 #include <cstdio> 9 #include <cstring> 10 #include <cstdlib> 11 #include <cmath> 12 #include <vector> 13 #include <map> 14 #include <set> 15 #include <sstream> 16 #include <string> 17 #include <stack> 18 #include <queue> 19 #include <iostream> 20 #include <algorithm> 21 using namespace std; 22 #define lp (p << 1) 23 #define rp (p << 1|1) 24 #define getmid(l,r) (l + (r - l) / 2) 25 #define MP(a,b) make_pair(a,b) 26 typedef long long ll; 27 const int INF = 1 << 30; 28 const int maxn = 1010; 29 30 int n,m,cnt; 31 int din[maxn],reb[maxn],dp[maxn]; 32 int vis[maxn],first[maxn],next[maxn * maxn],ver[maxn * maxn],ecnt; 33 string s,id1,id2; 34 char name[1500]; 35 map< string,int > mp; 36 37 void Add_edge(int u,int v){ 38 next[++ecnt] = first[u]; 39 ver[ecnt] = v; 40 first[u] = ecnt; 41 } 42 43 void Init(){ 44 memset(reb,0,sizeof(reb)); 45 memset(din,0,sizeof(din)); 46 memset(first,-1,sizeof(first)); 47 ecnt = cnt = 0; 48 mp.clear(); 49 } 50 51 int Solve(int p){ 52 if(dp[p]) return dp[p]; 53 int ans = 0; 54 for(int i = first[p]; i != -1; i = next[i]){ 55 int v = ver[i]; 56 ans = max(ans,Solve(v)); 57 } 58 return dp[p] = ans + reb[p]; 59 } 60 61 int DAG_max(){ 62 memset(dp,0,sizeof(dp)); 63 int ans = 0; 64 for(int i = 1; i <= cnt; ++i) 65 if(din[i] == 0) 66 ans = max(ans,Solve(i)); 67 return ans; 68 } 69 70 int main(){ 71 int a,b,len,flag; 72 scanf("%d",&n); 73 getchar(); 74 getchar(); 75 for(int Case = 1; Case <= n; ++Case){ 76 Init(); 77 while(getline(cin,s) != NULL){ 78 if(s[0] == '\0') 79 break; 80 istringstream sin(s); 81 sin >> name; 82 flag = 0; 83 int len = strlen(name); 84 for(int i = 0; i < len; ++i) 85 if(name[i] == '*') flag = 1; 86 name[len - 1 - flag] = '\0'; 87 id1 = name; 88 if(mp.find(id1) == mp.end()) 89 mp[id1]= ++cnt; 90 reb[mp[id1]] = flag; 91 while(sin >> id2){ 92 if(mp.find(id2) == mp.end()) 93 mp[id2] = ++cnt; 94 Add_edge(mp[id2],mp[id1]); 95 din[mp[id1]]++; 96 } 97 } 98 printf("Case %d: %d\n",Case,DAG_max()); 99 } 100 return 0; 101 }