51nod 1522 上下序列
题目描述
现在有1到n的整数,每一种有两个。要求把他们排在一排,排成一个2*n长度的序列,排列的要求是从左到右看,先是不降,然后是不升。
特别的,也可以只由不降序列,或者不升序列构成。
例如,下面这些序列都是合法的:
· [1,2,2,3,4,4,3,1];
· [1,1];
· [2,2,1,1];
· [1,2,3,3,2,1].
除了以上的条件以外,还有一些其它的条件,形如"h[xi] signi h[yi]",这儿h[t]表示第t个位置的数字,signi是下列符号之一:'=' (相等), '<' (小于), '>' (大于), '<=' (小于等于), '>=' (大于等于)。这样的条件有k个。
请计算一下有多少种序列满足条件。
Input
单组测试数据。
第一行有两个整数 n 和k (1≤n≤35, 0≤k≤100),表示数字的种类和限制条件的数目。
接下来k行,每一行的输入格式是这样的:"xi signi yi" (1≤xi,yi≤2*n),signi是上面五种符号中的一种。
Output
输出一个整数,表示有多少种序列满足条件。
Input示例
样例输入1
3 0
样例输入2
3 1
2 > 3
Output示例
样例输出1
9
样例输出2
1
题解
看了之后好懵逼啊……看了网上的题解,稍微理解了。
这道题主要的思路是区间DP。每次把两个相同的数字填进已有的序列[l, r]中。
但是由于题目中有这些限制,显然不能瞎填对吧……那么怎么判断能不能这样填呢?
方便起见,从大到小、从中间到两边填(因为原题要求中间比两边大)。
既然后填的、两边的数一定比先填的、中间的数小,那么只需判断当前要填的位置是否有“大于、大于等于、或等于已经填的区间内的元素”的限制就好了。另外,当前填的两个元素中,不能有“一个大于另一个”的限制。
具体来说,如果用dp[i][j]表示填满区间[i, j]的方案数,check(a, b, l, r) == 1 表示a、b不会“大于、大于等于、或等于[l, r]内的元素”且没有"a>b“或”b>a"的限制。
那么:
若都填到左边:if(check(i, i + 1, i + 2, j)) dp[i][j] += dp[i + 2][j]
若都填到右边:if(check(j - 1, j, i, j - 2)) dp[i][j] += dp[i][j - 2]
若两边各填一个: if(check(i, j, i + 1, j - 1)) dp[i][j] += dp[i + 1][j - 1]
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; #define INF 0x3f3f3f3f #define space putchar(' ') #define enter putchar('\n') template <class T> bool read(T &x){ char c; bool minus = 0; while(c = getchar(), c < '0' || c > '9') if(c == '-') minus = 1; else if(c == EOF) return 0; x = c - '0'; while(c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; if(minus) x = -x; return 1; } template <class T> void write(T x){ if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar('0' + x % 10); } const int N = 80, K = 105; int n, k; int lar [K], leq [K], equ [K]; ll dp ; void init(){ read(n), read(k); int a, b; char sig[2]; while(k--){ sig[0] = sig[1] = 0; read(a), scanf("%s", sig), read(b); if(sig[0] == '=') equ[a][++equ[a][0]] = b, equ[b][++equ[b][0]] = a; else{ if(sig[0] == '<') swap(a, b); if(sig[1] == '=') leq[a][++leq[a][0]] = b; else lar[a][++lar[a][0]] = b; } } } bool single_check(int a, int b, int l, int r){ for(int i = 1; i <= lar[a][0]; i++) if((lar[a][i] <= r && lar[a][i] >= l) || lar[a][i] == b) return 0; for(int i = 1; i <= leq[a][0]; i++) if(leq[a][i] <= r && leq[a][i] >= l) return 0; for(int i = 1; i <= equ[a][0]; i++) if(equ[a][i] <= r && equ[a][i] >= l) return 0; return 1; } bool check(int a, int b, int l, int r){ return single_check(a, b, l, r) && single_check(b, a, l, r); } int main(){ init(); for(int i = 1; i < 2 * n; i++) if(check(i, i + 1, 0, 0)) dp[i][i + 1] = 1; for(int len = 2; len <= 2 * n; len += 2) for(int i = 1; i + len - 1 <= 2 * n; i++){ int j = i + len - 1; if(check(i, j, i + 1, j - 1)) dp[i][j] += dp[i + 1][j - 1]; if(check(i, i + 1, i + 2, j)) dp[i][j] += dp[i + 2][j]; if(check(j - 1, j, i, j - 2)) dp[i][j] += dp[i][j - 2]; } write(dp[1][2 * n]), enter; return 0; }
- 51Nod-1522-上下序列
- 51nod--1126 求递推序列的第N项
- 51Nod 1062 序列中最大的数
- 【51nod】1251 Fox序列的数量
- 【贪心】51Nod 1476 括号序列的最小代价
- 51nod 1202 子序列个数【dp】好题!
- 51Nod-1126-求递推序列的第N项
- 51nod 1478 括号序列的最长合法子段【思维+前缀和+优先队列】好题!
- [51NOD]1126 求递推序列的第N项 [线性递推关系与矩阵乘法]
- 51nod 1062 序列中最大的数
- 51nod 1126 求递推序列的第N项 (矩阵快速幂)
- 51nod 1258 序列求和 V4
- 51nod 1126 求递推序列的第N项
- 51nod 1134 【最长上升子序列】
- 【51Nod】1510 最小化序列 贪心+动态规划
- 51nod 1400 序列分解 阻止我的只有读题能力了
- 51Nod 1376 最长递增子序列的数量 (DP+BIT)
- 51nod 1202 子序列个数
- 51nod 1134 最长递增子序列(dp) 时间复杂度为O(nlogn)
- 51nod 1218 最长递增子序列 V2 【最长递增+复杂判断】