dp+分类讨论 Gym 101128E
题目链接:http://codeforces.com/gym/101128
感觉这个人写的不错的(我只看了题目大意):http://blog.csdn.net/v5zsq/article/details/61428924
Description
n个小木条,一段前面有一个小箭头,给出第一个小木条的非箭头端端点横坐标以及每个小木条箭头端的坐标,现在要从下往上把这n’个木条按顺序叠放好,要求相邻两个小木条必须有一个共同端点且有交叠部分,问小木条有多少种放法
Input
第一行一整数n表示木条数量,之后输入n+1个整数分别表示第一个小木条非箭头端点和n个小木条的箭头端点横坐标(1<=n < 2000,每个端点横坐标是一个介于1~n+1之间的整数,保证第一个小箭头朝右)
思路:
定义dp(i,j)表示目前是第i个木头,他的区间是[min(j, a[i]), max(j, a[i])].
然后我们判断一下a[i-1]的范围和这个区间的范围,即分类讨论五种。
然后为了维护其中两种,所以我们这里还用了一个sum[j]数组表示dp[i][0]~dp[i][j]的和,然后这样就可以O(1)的到了
注意特判n=1的时候(因为这个wa了一发)
复杂度O(n^2)
//看看会不会爆int!数组会不会少了一维! //取物问题一定要小心先手胜利的条件 #include <bits/stdc++.h> using namespace std; #pragma comment(linker,"/STACK:102400000,102400000") #define LL long long #define ALL(a) a.begin(), a.end() #define pb push_back #define mk make_pair #define fi first #define se second #define haha printf("haha\n") const int maxn = 2000 + 5; const LL mod = 2147483647; LL dp[maxn][maxn]; LL sum[maxn]; int a[maxn]; int n; int main(){ while(scanf("%d", &n) == 1){ for (int i = 0; i <= n; i++){ scanf("%d", a + i); } if (n == 1) { printf("1\n"); continue; } memset(dp, 0, sizeof(dp)); memset(sum, 0, sizeof(sum)); for (int i = 1; i <= n + 1; i++){ if (i == a[0] || i == a[1]) sum[i] = dp[1][i] = 1; sum[i] = 1; } int len = n + 1; for (int i = 2; i <= n; i++){ for (int j = 1; j <= len; j++){ if (j == a[i]) continue; int lb = min(j, a[i]), rb = max(j, a[i]); //printf("lb = %d rb = %d\n", lb, rb); if (a[i - 1] < lb){ dp[i][j] += dp[i - 1][rb]; } else if (a[i - 1] == lb){ dp[i][j] += (sum[len] - sum[lb] + mod) % mod; } else if (a[i - 1] > rb){ dp[i][j] += dp[i - 1][lb]; } else if (a[i - 1] == rb){ dp[i][j] += sum[rb - 1]; } else { dp[i][j] += dp[i - 1][lb] + dp[i - 1][rb]; } if (dp[i][j] >= mod) dp[i][j] %= mod; //printf("dp[%d][%d] = %lld\n", i, j, dp[i][j]); } for (int j = 1; j <= len; j++){ sum[j] = (sum[j - 1] + dp[i][j]) % mod; } } LL ans = 0; for (int i = 1; i <= len; i++){ ans = (ans + dp[n][i]) % mod; } cout << ans << endl; } return 0; }