DP-k倍区间

来源:http://blog.youkuaiyun.com/za30312/article/details/69681146

问题描述
  给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。


  你能求出数列中总共有多少个K倍区间吗?
输入格式
  第一行包含两个整数N和K。(1 <= N, K <= 100000)
  以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)
输出格式
  输出一个整数,代表K倍区间的数目。
样例输入
5 2
1
2
3
4
5
样例输出
6
数据规模和约定
  峰值内存消耗(含虚拟机) < 256M
  CPU消耗 < 2000ms




  请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。


  注意:
  main函数需要返回0;
  只使用ANSI C/ANSI C++ 标准;
  不要调用依赖于编译环境或操作系统的特殊函数。
  所有依赖的函数必须明确地在源文件中 #include <xxx>
  不能通过工程设置而省略常用头文件。


  提交程序时,注意选择所期望的语言类型和编译器类型。



复杂度n^2的解法

#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
int num;
int buf[100010];
long long int dp[100010];

int main()
{
    int i,j;
    int res = 0;
    cin >> n;
    cin >> k;
    for(i = 1;i <= n;i++)
    {
        cin >> buf[i];
		dp[i] = (dp[i-1] + buf[i])%k;
    }
    for(i = 1;i <= n;i++)
    {
		int t;
        for(j = n;j >= i;j--)
        {
			t = dp[j]-dp[i-1];
            if((t) == 0)
            {
                res++;
            }
			
        }
    }
	cout << res;
    system("pause");
    return 0;
    


}
优化后
#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
int num;
int buf[100010];
long long int dp[100010];

int main()
{
    int i,j;
    int res = 0;
    cin >> n;
    cin >> k;
    for(i = 1;i <= n;i++)
    {
        cin >> buf[i];
    }
    for(i = 1;i <= n;i++)
    {
        buf[i] = (buf[i] - buf[i-1])%k;//buf[i] == sum[i]
    }
    for(i = 0;i <= n;i++)
    {
        res += dp[buf[i]]++;
    }
    cout << res;
    system("pause");
    return 0;
    
    


}

<think>嗯,用户想了解区间动态规划的提高与进阶技巧。首先,我需要回忆一下区间DP的基础知识,因为进阶技巧通常是建立在基础之上的。区间DP通常用于处理涉及区间的问题,比如合并、分割或者处理子区间的最优解。常见的例子有石子合并、最长回文子序列等。 用户提供的引用中有几个关键点。引用[1]提到区间DP的状态设计、转移和时间复杂度,这可能涉及到状态转移方程的优化。引用[2]中的代码示例展示了倒序遍历i来确保状态更新正确,这可能与状态转移的顺序有关。引用[3]里的状态转移方程比较复杂,涉及多个项的组合,可能需要分情况讨论或者使用辅助数组。引用[4]中的记忆化搜索方法,说明递归方式可能在某些情况下更直观。引用[5]虽然只是学习路线的介绍,但提示用户可能需要系统的知识点讲解。 接下来,我需要整理区间DP的进阶技巧。首先,环形区间处理,比如将数组扩展成两长度来模拟环形结构。然后,回文子序列问题可能需要特殊的区间划分方式。辅助维度是另一个方向,比如增加一维来记录更多状态信息,例如切割次数或某种标记。另外,状态转移的优化,比如四边形不等式,可以降低时间复杂度。还有可能涉及复杂的状态转移方程,比如结合前缀和或数学变换。 还要考虑用户可能需要具体的例子和代码实现来理解这些技巧。比如,环形石子合并问题,如何通过扩展数组来处理。回文问题中的状态转移如何设计。辅助维的添加如何影响状态转移方程的结构。此外,记忆化搜索和迭代方式的对比,可能在实现上有不同的技巧。 需要注意的是,用户可能已经了解基础,所以需要跳过基础部分,直接进入提高内容。同时,结合引用中的例子,如多边形三角剖分、焦虑值计算等,可以丰富讲解的实例。最后,生成相关问题时要涵盖应用场景、优化方法、与其他DP的区别等,帮助用户进一步学习。</think>### 区间动态规划提高与进阶技巧详解 #### 一、区间DP核心思想与特征回顾 区间动态规划通过定义$dp[i][j]$表示区间$[i,j]$的最优解,通过合并子区间信息进行递推。其核心特征包括: 1. **状态设计**:必须包含区间端点$i,j$,例如$dp[i][j]$表示合并石子堆$i$到$j$的最小代价 2. **转移方向**:通常以区间长度$len$为阶段,从小到大递推 3. **时间复杂度**:一般为$O(n^3)$,优化后可达$O(n^2)$[^1] #### 二、进阶技巧详解 ##### 1. 环形区间处理(破环成链) **问题场景**:环形石子合并、环形字符串处理 **实现方法**:将数组复制为双长度$[a_1,a_2,...,a_n,a_1,a_2,...,a_n]$,转化为线性问题后取所有长度为$n$的区间最优解 ```python # 环形石子合并示例 n = len(stones) extended = stones * 2 for i in range(2*n-2, -1, -1): for j in range(i+1, min(i+n, 2*n)): dp[i][j] = min(dp[i][k] + dp[k+1][j] for k in range(i,j)) + sum(extended[i:j+1]) result = min(dp[i][i+n-1] for i in range(n)) ``` ##### 2. 回文相关问题的特殊处理 **状态转移优化**:当$s[i]=s[j]$时直接继承内部结果 **典型问题**:最长回文子序列、回文分割 $$dp[i][j] = \begin{cases} dp[i+1][j-1] + 2, & s[i]=s[j] \\ \max(dp[i+1][j], dp[i][j-1]), & s[i]\neq s[j] \end{cases}$$[^2] ##### 3. 增加辅助维度 **应用场景**:需要记录额外信息时 **示例**:带权区间划分(增加切割次数维度) ```python # 三维DP定义:dp[i][j][k]表示区间[i,j]划分k次的最小代价 for k in 1..m: for i in 0..n-1: for j in i+1..n: dp[i][j][k] = min(dp[i][p][k-1] + cost(p,j) for p in i..j) ``` ##### 4. 状态转移优化技巧 **四边形不等式优化**:当代价函数满足单调性时,可将时间复杂度从$O(n^3)$优化至$O(n^2)$ **最优决策点单调性**:记录转移点$pos[i][j]$,使$pos[i][j-1] \leq pos[i][j] \leq pos[i+1][j]$ ##### 5. 记忆化搜索实现 **优势**:处理复杂状态转移更直观,避免迭代顺序问题 **实现要点**: - 使用备忘录缓存计算结果 - 处理边界条件优先 ```cpp // 记忆化搜索示例(字符串回文划分) int dfs(int l, int r) { if(l > r) return 0; if(memo[l][r] != -1) return memo[l][r]; if(s[l] == s[r]) res = dfs(l+1, r-1) + 2; else res = max(dfs(l+1,r), dfs(l,r-1)); return memo[l][r] = res; }[^4] ``` #### 三、经典题型解析 | 问题类型 | 状态转移特征 | 优化方法 | |-----------------------|-----------------------------------|-------------------------| | 石子合并类问题 | $dp[i][j] = \min(dp[i][k] + dp[k+1][j]) + sum(i,j)$ | 前缀和优化 | | 回文子序列问题 | 对称性转移 | 空间压缩 | | 多边形三角剖分 | $dp[i][j] = \min(dp[i][k] + dp[k][j] + cost)$ | 环形处理 | | 带权区间划分 | 辅助维度记录划分次数 | 决策单调性优化 | #### 四、调试与优化实践 1. **可视化DP表**:打印二维数组观察状态转移过程 2. **边界测试**:特别注意$len=1,2$时的初始化 3. **时间复杂度分析**:通过代码结构判断实际计算量 4. **空间优化**:使用滚动数组(如$dp[2][n]$)处理大规模数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值