POJ 1952 DP LIS求方案数
2015-09-04 08:51
316 查看
题意:给一个序列,求最长不上升子序列长度以及有多少个这样的子序列。注意:看起来相同的子序列被算作一种方案。例如:421421的方案为442,441,422,421,411五种。
对于需要求方案数的LIS,求长度时就老老实实用O(n^2)的DP算法吧。
f[i]表示以i为最后一个数的最长不上升子序列,if(a[i] <= a[j]) f[i] = max(f[i], f[j]+1),而max{f[i]}就是第一问的答案。这个很好说。
主要是方案数,题目要求看起来相同的是被算作一种的,我们先设d[i][j]为以i结尾,不上升子序列的长度为j的方案数。如果没有之前那个要求,那么d[i][j] = ∑ d[k][j-1](k < i && a[i] <= a[j])。
但是考虑到题目的要求,我们需要进行判重,可以这么想:在一个序列a1,a2……an中,如果有ai == aj(i < j),那么d[j][t]肯定包括所有的d[i][t](0<t<=f[i])。也就是说我们对每个数本身进行判重,对于k,需要逆序枚举,如果枚举过将vis[a[k]]标记为1,我们只讲vis[a[k]] == 0的加到d[i][j]中即可,题目中也说明了数的大小是1<<16以内的,所以bool型数组是开得下的。
另外需要注意,在递归的过程中,如果d[i1][j1]枚举过k,把vis[a[k]]设为1,但是在另一个状态d[i2][j2](i2>k,i1>k)中,a[k]这个数是可以被再次访问的,就是说,对于每个d[i][j]都有独立的一个vis数组,但是在递归中是开不下的,会RE,所以就需要一个小技巧:每次递归开始就memset一下,相当于新声明了一个vis数组,当需要d[i][j] += d[k][j-1]时,先递归访问dp(k,j-1),再将vis[a[k]]设为1即可。
最后输出答案是d[n+1][ans+1],我们假定a[n+1]是一个最小的数,最长不上升子序列长度加1。
或者直接写个循环,把长度为ans的累加即可,这个与dp(n+1,ans+1)这一层递归是等价的。
对于需要求方案数的LIS,求长度时就老老实实用O(n^2)的DP算法吧。
f[i]表示以i为最后一个数的最长不上升子序列,if(a[i] <= a[j]) f[i] = max(f[i], f[j]+1),而max{f[i]}就是第一问的答案。这个很好说。
主要是方案数,题目要求看起来相同的是被算作一种的,我们先设d[i][j]为以i结尾,不上升子序列的长度为j的方案数。如果没有之前那个要求,那么d[i][j] = ∑ d[k][j-1](k < i && a[i] <= a[j])。
但是考虑到题目的要求,我们需要进行判重,可以这么想:在一个序列a1,a2……an中,如果有ai == aj(i < j),那么d[j][t]肯定包括所有的d[i][t](0<t<=f[i])。也就是说我们对每个数本身进行判重,对于k,需要逆序枚举,如果枚举过将vis[a[k]]标记为1,我们只讲vis[a[k]] == 0的加到d[i][j]中即可,题目中也说明了数的大小是1<<16以内的,所以bool型数组是开得下的。
另外需要注意,在递归的过程中,如果d[i1][j1]枚举过k,把vis[a[k]]设为1,但是在另一个状态d[i2][j2](i2>k,i1>k)中,a[k]这个数是可以被再次访问的,就是说,对于每个d[i][j]都有独立的一个vis数组,但是在递归中是开不下的,会RE,所以就需要一个小技巧:每次递归开始就memset一下,相当于新声明了一个vis数组,当需要d[i][j] += d[k][j-1]时,先递归访问dp(k,j-1),再将vis[a[k]]设为1即可。
最后输出答案是d[n+1][ans+1],我们假定a[n+1]是一个最小的数,最长不上升子序列长度加1。
或者直接写个循环,把长度为ans的累加即可,这个与dp(n+1,ans+1)这一层递归是等价的。
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; int n, ans, a[5005], f[5005], d[5005][5005]; bool vis[(1<<16)+5]; int dp(int i, int j){ memset(vis, 0, sizeof vis); if(d[i][j]) return d[i][j]; if(j == 1) return d[i][j] = 1; for(int k = i-1; k; k--){ if(!vis[a[k]] && a[k] > a[i] && f[k] == j-1) d[i][j] += dp(k, j-1), vis[a[k]] = 1; } return d[i][j]; } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++){ scanf("%d", a+i); f[i] = 1; for(int j = 1; j < i; j++) if(a[j] > a[i]) f[i] = max(f[i], f[j]+1); ans = max(ans, f[i]); } printf("%d %d", ans, dp(n+1, ans+1)); }
相关文章推荐
- 不用加减乘除实现加法
- Ubuntu中启用关闭Network-manager网络设置问题! 【Server版本】
- 经典排序算法锦集(深度好文)
- PHP基础----PHP 会话管理----24操作 Session
- 魔法训练
- 如何在JavaScript捕获鼠标事件
- P2P平台很赚钱么?
- PHP基础----PHP 会话管理----23操作 Cookie
- P2P平台很赚钱么?
- hdu 4006__The kth great number(STL)
- 九大排序算法
- 使用maven私服构建 springmvc hibernate4 mysql 项目
- 二分图定理
- PHP基础----PHP 会话管理----22Cookie 和 Session 的概念与区别
- AVD启动不了 ANDROID_SDK_HOME is defined but could not find *.ini
- 虚拟机下NAT模式无法上网
- Python __dic__巧妙用法
- oracle数据恢复:利用ctl,dbf,log
- Meta http-equiv属性详解
- kestrel源代码阅读