標籤:get def str 位置 push problem set bool 16px
題目傳送門:http://codeforces.com/problemset/problem/567/F大致題意:你有$1$到$N$的所有正整數每個數兩個,現在需要你將它排成一個序列,使得序列為單峰序列(存在一個$j \in [1,2N]$滿足$a_1 \leq a_2 \leq .. \leq a_j \geq a_{j+1} ... \geq a_{2N}$),並且滿足輸入中給出的$K$個約束條件。$N \leq 35 , K \leq 100$
因為序列要滿足單峰,所以考慮從小到大在沒有加入數位序列位置的最左和最右加入元素,這樣序列將會一直滿足單峰性質
考慮狀態$f_{p,q}$表示左邊已經填了$p$個,右邊填了$q$個的方案(其實可以壓掉其中一維),考慮第$i$個數位轉移,有$3$種:
$a:$將這兩個$i$都放在序列最左邊;$b:$將這兩個$i$放在序列最右邊;$c.$將這兩個$i$一個放一邊。
對於$K$個約束條件我們可以在加入的時候直接判斷:當前加入的兩個數相等,沒加入的位置的數必定比當前加的數大,而其他已經填好的地方一定比當前的數小。
最後的答案是$\frac{\sum\limits_{i=0}^{2n}f_{i,2n-i}}{3}$,除3是因為對於最後一次加入,$abc$三種方案都是可行的,但是我們只能算一種。
注意判斷約束條件時的細節,約束條件的儲存可以使用類似鄰接表的方法,在加入約束條件時如加入無向邊一樣變成兩個約束條件,判斷起來會方便一些。
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 5 int ans[72][72] , numOp[72]; 6 7 vector < int > num[72] , op[72]; 8 map < string , int > opToNum; 9 10 bool ifOK(int l , int r , int que1 , int que2){ 11 //特別注意判斷que1位置與que2位置有聯絡的情況 12 for(int i = 0 ; i < numOp[que1] ; i++) 13 switch(op[que1][i]){ 14 case 1: 15 if(num[que1][i] < r && num[que1][i] > l || num[que1][i] == que2) 16 return 0; 17 break; 18 case 2: 19 if(num[que1][i] < r && num[que1][i] > l && num[que1][i] != que2) 20 return 0; 21 break; 22 case 3: 23 if(!(num[que1][i] == que2)) 24 return 0; 25 break; 26 case 4: 27 if(!(num[que1][i] <= r && num[que1][i] >= l || num[que1][i] == que2)) 28 return 0; 29 break; 30 case 5: 31 if(!(num[que1][i] < r && num[que1][i] > l) || num[que1][i] == que2) 32 return 0; 33 } 34 for(int i = 0 ; i < numOp[que2] ; i++) 35 switch(op[que2][i]){ 36 case 1: 37 if(num[que2][i] < r && num[que2][i] > l || num[que2][i] == que1) 38 return 0; 39 break; 40 case 2: 41 if(num[que2][i] < r && num[que2][i] > l && num[que2][i] != que1) 42 return 0; 43 break; 44 case 3: 45 if(!(num[que2][i] == que1)) 46 return 0; 47 break; 48 case 4: 49 if(!(num[que2][i] <= r && num[que2][i] >= l || num[que2][i] == que1)) 50 return 0; 51 break; 52 case 5: 53 if(!(num[que2][i] < r && num[que2][i] > l) || num[que2][i] == que1) 54 return 0; 55 } 56 return 1; 57 } 58 59 main(){ 60 opToNum.insert(make_pair(">" , 1)); 61 opToNum.insert(make_pair(">=" , 2)); 62 opToNum.insert(make_pair("=" , 3)); 63 opToNum.insert(make_pair("<=" , 4)); 64 opToNum.insert(make_pair("<" , 5)); 65 int N , K; 66 for(cin >> N >> K ; K ; K--){ 67 int a , b , t; 68 string s; 69 cin >> a >> s >> b; 70 t = opToNum.find(s)->second; 71 if(a == b) 72 if(t == 1 || t == 5){ 73 cout << 0; 74 return 0; 75 } 76 else 77 continue; 78 num[a].push_back(b); 79 op[a].push_back(t); 80 num[b].push_back(a); 81 op[b].push_back(6 - t); 82 numOp[a]++; 83 numOp[b]++; 84 } 85 ans[0][0] = 1; 86 for(int i = 1 ; i <= N ; i++){ 87 //DP 88 for(int j = 2 * i ; j >= 2 ; j--) 89 if(ans[j - 2][2 * i - j] && ifOK(j , 2 * N - 2 * i + j + 1 , j , j - 1)) 90 ans[j][2 * i - j] += ans[j - 2][2 * i - j]; 91 for(int j = 2 * i ; j >= 2 ; j--) 92 if(ans[2 * i - j][j - 2] && ifOK(2 * i - j , 2 * N - j + 1 , 2 * N - j + 1 , 2 * N - j + 2)) 93 ans[2 * i - j][j] += ans[2 * i - j][j - 2]; 94 for(int j = 1 ; j < 2 * i ; j++) 95 if(ans[2 * i - j - 1][j - 1] && ifOK(2 * i - j , 2 * N - j + 1 , 2 * i - j , 2 * N - j + 1)) 96 ans[2 * i - j][j] += ans[2 * i - j - 1][j - 1]; 97 } 98 int all = 0; 99 for(int i = 0 ; i <= 2 * N ; i++)100 all += ans[i][2 * N - i];101 cout << all / 3;102 return 0;103 }
51NOD1522&CF567F 上下序列/Mausoleum DP