標籤:style blog http color os io 資料 for
做多校的時候遇見一個求拓撲排序數量的題,就順便來寫了一下。
題意:
你有個朋友是KOF的狂熱粉絲,他有一個對其中英雄的強弱比較,讓你根據這些比較關係來給這些英雄排名。問一共有多少種排名方式。
思路:
用dp[S]記錄目前狀態的數量。 S表示拓撲排序中當前階段已經被排序的點的集合。然後就可以枚舉當前排序的點,轉移的條件是這個點的所有前驅都被排序,而且這個點沒被排序。然後轉移就好了,最終狀態就是所有點都完成排序。
代碼:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cstdlib> 5 #include <cmath> 6 #include <algorithm> 7 #include <string> 8 #include <queue> 9 #include <stack>10 #include <vector>11 #include <map>12 #include <set>13 #include <functional>14 #include <time.h>15 16 using namespace std;17 18 const int INF = 1<<30;19 const int MAXN = 16;20 21 int dp[1<<MAXN];22 int pre[MAXN]; //記錄每個點的前驅集合23 char name[MAXN][MAXN]; //記錄每個人物的名字24 char str[2][MAXN];25 int n, m;26 27 void input() {28 //讀資料及其之間的關係。29 //並且把他們存起來,給每一個名字一個編號30 //處理出來每個點的前驅存在pre[]中31 int u, v;32 n = 0;33 memset(pre, 0, sizeof(pre));34 for (int i = 0; i < m; i++) {35 scanf("%s%s", str[0], str[1]);36 37 for (u = 0; u < n; u++) //找到這個字串38 if (strcmp(name[u], str[0])==0)39 break;40 if (u==n) strcpy(name[n++], str[0]); //如果沒找到,插入這個字串41 42 for (v = 0; v < n; v++)43 if (strcmp(name[v], str[1])==0)44 break;45 if (v==n) strcpy(name[n++], str[1]);46 47 pre[v] |= (1<<u); //u是v的前驅,所以,把u加進v的前驅集合48 }49 //for (int i = 0; i < n; i++) printf("%d ", pre[i]); puts(""); //輸出前驅集合50 }51 52 void solve() {53 //動態規劃54 //枚舉每個狀態(已經有拓撲序的集合),然後枚舉假如這個集合的點55 memset(dp, 0, sizeof(dp));56 dp[0] = 1; //初始狀態57 58 for (int S = 0; S < (1<<n); S++) if (dp[S]!=0) { //剪枝59 for (int i = 0; i < n; i++) if (((S&pre[i])==pre[i]) && !(S&(1<<i))) { //i的前驅全部在此狀態中,並且i不在60 dp[S|(1<<i)] += dp[S];61 }62 }63 64 printf("%d\n", dp[(1<<n)-1]); //最終狀態時所有點都被排序65 }66 67 int main() {68 #ifdef Phantom0169 freopen("ZJU1346.txt", "r", stdin);70 #endif //Phantom0171 72 while (scanf("%d", &m)!=EOF) {73 input();74 solve();75 }76 77 return 0;78 }ZJU 1346