51NOD1522 上下序列/CF567F Mausoleum DP

大致题意:你有$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 }

 

转载于:https://www.cnblogs.com/Itst/p/9748375.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值