数位dp的一种姿势

最近跟 Rivendell 大神学了一种新的姿势的数位 dp ,非常好用。
这种数位 dp 大致是这样的: f[i]...[2] 表示的是i位的时候,中间是你需要保存的一些东西,最后的那一维 1 表示到第i位的时候前面的数都跟n的前面的位置的数相等,0表示小于(如果需要的话还可以记一下大于的)。
设计状态的时候就是需要什么就设什么,转移的过程中枚举所有的状态转移。
写法看这

/* translation: 给出一列数列,按照不递减的顺序。并且给出若干查询操作,查询某一区间出现频率最频繁的次数是多少? solution: RMQ的ST算法 首先设置数组dp[i][j]表示从i开始长度为2^i次方的查询结果。则根据动态规划有: dp[i][j] = max(dp[i][j-1], dp[i+(len >> 1)][j-1])。 然后根据数据不递减的特点,可以将其“游标编程”。并且在查询时对两端的点进行单独判断。然后按照ST的 查询操作即可。 note: *: 习得ST算法解决RMQ的正确姿势ORZ date: 2016.11.16 */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 100000 + 5; int n, q; int a[maxn]; int pos[maxn], p; int Left[maxn], Right[maxn]; int dp[maxn][50]; void RMQ_init() { memset(dp, 0, sizeof(dp)); for(int i = 0; i <= p; i++) dp[i][0] = Right[i] - Left[i] + 1; for(int j = 1; (1 << j) <= p; j++){ int len = 1 << j; for(int i = 0; i + len - 1 <= p; i++) dp[i][j] = max(dp[i][j-1], dp[i+(len >> 1)][j-1]); } } int query(int L, int R) { int k = 0; while((1 << (k+1)) <= R - L + 1) k++; return max(dp[L][k], dp[R-(1<<k)+1][k]); } int main() { //freopen("in.txt", "r", stdin); while(~scanf("%d%d", &n, &q) && n){ memset(Left, 0, sizeof(Left)); memset(Right, 0, sizeof(Right)); memset(pos, 0, sizeof(pos)); a[0] = maxn * 10; p = 0; for(int i = 1; i <= n; i++){ scanf("%d", &a[i]); if(a[i] != a[i-1]){ p++; pos[i] = p; Left[p] = Right[p] = i; }else{ pos[i] = p; Right[p]++; } } RMQ_init(); int from, to; while(q--){ scanf("%d%d", &from, &to); int ans; if(pos[from] == pos[to]) ans = to - from + 1; else if(pos[to] - pos[from] == 1){ ans = max(Right[pos[from]] - from + 1, to - Left[pos[to]] + 1); }else{ ans = max(Right[pos[from]] - from + 1, to - Left[pos[to]] + 1); ans = max(query(pos[from] + 1, pos[to] - 1), ans); } printf("%d\n", ans); } } return 0; }分模块讲解
最新发布
03-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值