动态规划

区间DP(超详细!!!)

匿名 (未验证) 提交于 2019-12-03 00:12:02
一、问题 给定长为n的序列a[i],每次可以将连续一段回文序列消去,消去后左右两边会接到一起,求最少消几次能消完整个序列,n≤500。 f[i][j]表示消去区间[i,j]需要的最少次数。 则 ; 若a[i]=a[j],则还有 。 这里实际上是以区间长度为阶段的,这种DP我们通常称为区间DP。 区间DP的做法较为固定,即 枚举区间长度 ,再枚举左端点,之后枚举区间的断点进行转移。 二、概念 区间类型动态规划是线性动态规划的拓展,它在分阶段划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。(例:f[i][j]=f[i][k]+f[k+1][j]) 区间类动态规划的特点: 合并:即将两个或多个部分进行整合。 特征 :能将问题分解成为两两合并的形式。 求解:对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,最后将左右两个部分的最优值进行合并得到原问题的最优值。 三、例题 【例题一】石子合并: 【问题描述】 将n(1≤n≤200)堆石子绕 圆形 操场摆放,现要将石子有次序地合并成一堆。规定每次只能选相邻的两堆石子合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。 (1)选择一种合并石子的方案,使得做n-1次合并,得分的总和最小。 (2)选择一种合并石子的方案,使得做n-1次合并,得分的总和最大。 【样例输入】 4 4 5 9 4 【样例输出】

通过一个动态规划的例子来体会时间复杂度的不同

匿名 (未验证) 提交于 2019-12-03 00:11:01
public class Main { public static void main ( String [] args ) { // write your code here int N = 10000 ; int [] A = new int [ N ]; for ( int i = 0 ; i < N ; i ++){ A [ i ] = ( int )( Math . random () * N + 1 ); } long startTime = System . nanoTime (); int res = MaxSubsequenceSum_4 ( A , N ); long endTime = System . nanoTime (); System . out . println ( res ); System . out . println ( "程序运行时间: " +( endTime - startTime )+ "ns" ); } //Algorithm 1 O(N^3) public static int MaxSubsequenceSum_1 ( final int A [], int N ){ int ThisSum , MaxSum = 0 ; for ( int i = 0 ; i < N ; i ++){ for ( int j = i ; j

【搜索】【动态规划】【杂题】――洛谷P1514.引水入城

匿名 (未验证) 提交于 2019-12-03 00:11:01
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=510; 4 int n,m; 5 int val[N][N]; 6 int l[N][N],r[N][N]; 7 int vis[N][N]; 8 int px[5]{1,0,-1,0},py[5]{0,-1,0,1}; 9 struct sig{ 10 int a,b; 11 }sg[N]; 12 bool cmp(sig x,sig y){ 13 return x.a>y.a; 14 } 15 void dfs(int x,int y){ 16 vis[x][y]=1; 17 for(int i=0;i<=3;i++){ 18 int tx=x+px[i],ty=y+py[i]; 19 if(tx>n||tx<=0||ty>m||ty<=0) continue; 20 if(val[tx][ty]<val[x][y]){ 21 if(!vis[tx][ty]) dfs(tx,ty); 22 l[x][y]=min(l[x][y],l[tx][ty]); 23 r[x][y]=max(r[x][y],r[tx][ty]); 24 } 25 } 26 } 27 int read(){ 28 int x=0,f=1; 29 char c=getchar();

动态规划之背包九讲

匿名 (未验证) 提交于 2019-12-03 00:09:02
背包九讲 背包九讲链接: https://www.cnblogs.com/jbelial/articles/2116074.html 01背包: 题目链接: https://www.acwing.com/problem/content/2/ 二维数组优化前: 1 #include <iostream> 2 3 4 5 using namespace std; 6 7 const int N = 1e3 + 10; 8 int v[N], w[N]; 9 int f[N][N]; 10 11 int main() { 12 int N, V; 13 cin >> N >> V; 14 for (int i = 1; i <= N; i++) cin >> v[i] >> w[i]; 15 for (int i = 1; i <= N; i++) { 16 for (int j = 1; j <= V; j++) { 17 f[i][j] = f[i - 1][j]; 18 if (j >= v[i]) f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]); 19 } 20 } 21 cout << f[N][V] << endl; 22 return 0; 23 } View Code 优化后: #include

动态规划例题

 ̄綄美尐妖づ 提交于 2019-12-03 00:05:49
1.约瑟夫问题的变形(LA 3882) 题意: \(n\) 个数排成一个圈。第一次删除 \(m\) ,以后每数 \(k\) 个数删除一次,求最后一个被删除的数。 f[1]=0;//最后剩下的人为0 //然后一步一步回到它一开始的编号 for(int i=2;i<=n;++i) { f[i]=(f[i-1]+k)%i;//每一轮都会重新编号 } int ans=(m-k+1+f[n])%n;//beginpos+k=m-> beginpos=m-k,又要把f[n]的编号加上一 if(ans<=0) ans+=n; printf("%d\n",ans); 2.王子和公主 \((UVa10635)\) 题意: 一个王子和公主在 \(n*n\) 的格子中行走,这些格子是有 \(1....n^2\) 的编号的。现在给定 \(p+1\) 个数,再给定 \(q+1\) 个数,公主和王子可以选择其中某些格子行走,求他们最多能走几个相同的格子。 分析: 思路很巧妙,表面看本题是一个 \(LCS\) 问题 注意到序列中的数均不相同,因此可以把A中的数重新编号为 \(1\) ~ \(p+1\) 例如: \(A={1,7,5,4,8,3,9},B={1,4,3,5,6,2,8,9}\) ,因此可以把 \(A\) 重新编号为 \({1,2,3,4,5,6,7}\) ,而 \(B\) 就应为 \({1,4,6

关于递归和动态规划的简单理解

匿名 (未验证) 提交于 2019-12-03 00:05:01
简单的来说,递归就是一个概念能够用自身来解释,比如说一本字典,每个字词的解释是依靠字典中其他的字词来解释的。一般来说,计算机中遇到的递归问题大多是把一个问题分解成规模更小的子问题求解,再进行合并。 一个具有递归性质的问题,大多具有两个特征,第一个是状态转移方程也就是递归方程,比如在求解阶乘时,n!=n*(n-1)!,就将求解n的阶乘转换为求解n-1的阶乘。第二个特征就是终止条件,一个递归是一类问题的求解,必定有一个结果 无法一只递归下去,有一个结束条件,也就是当问题规模简化的足够小的时候可以直接的出答案。在求解阶乘时,当问题过为1的时候就直接输出1. int f ( n ) { if ( n == 1 ) //终止条件 { return 1 ; } else { return n * f ( n - 1 ); //递归方程 } } 递归执行时,递归函数会被反复地调用,一层一层的进入,到达终止条件后,再一层一层出来,为了保证结果的正确性,每一层函数的运算结果和状态都必须保存在系统所分配的栈里面,当数据量很大的时候,递归所占用的空间和运行时间会非常恐怖,效率也很低 这里再介绍一种和递归类似的但执行效率更高的动态规划算法。 递归算法是从顶置低求解问题,而・动态规划算法是从低置顶求解问题,同样也需要状态转移方程方程,和初始条件,相较于递归算法的优势,动态规划算法不需要反复调用自身函数

Leetcode之动态规划(DP)专题-647. 回文子串(Palindromic Substrings)

匿名 (未验证) 提交于 2019-12-03 00:00:02
Leetcode之动态规划(DP)专题-647. 回文子串(Palindromic Substrings) 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。 示例 1: 输入: "abc" 输出: 3 解释: 三个回文子串: "a", "b", "c". 示例 2: 输入: "aaa" 输出: 6 说明: 6个回文子串: "a", "a", "a", "aa", "aa", "aaa". 注意: 输入的字符串长度不会超过1000。 dp: 定义: dp[i][j]表示在从i开始到j结束这段字符串里,如果是回文串,则dp[i][j]=1,不是则dp[i][j]=0; 状态转移方程: if((s.charAt(i)==s.charAt(j)) && ((j-i<=2) || dp[i+1][j-1]==1)){   dp[i][j] = 1; } 举例解释: "aba"         (只要长度为3,且左等于右,不管中间是什么,都是回文字符串) 再举一种情况  "abba" class Solution { public int countSubstrings(String s) { int res = 0; int n = s.length(); int[][] dp = new int

从递归到memo到动态规划

匿名 (未验证) 提交于 2019-12-03 00:00:02
//memo,记忆化搜索 class Solution { int[][] memo; public boolean wordBreak(String s, List<String> wordDict) { int n=s.length(); memo=new int[n+1][n+1]; for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) memo[i][j]=-1; return breakWord(s,wordDict,0,n); } public boolean breakWord(String s,List<String> wordDict,int l,int r) { if(r==0) return true; if(memo[l][r]!=-1) return memo[l][r]==1; memo[l][r]=0; for(int i=r-1;i>=0;i--) { if(helper(s.substring(i,r),wordDict)&&breakWord(s,wordDict,l,i)) { memo[l][r]=1; break; } } return memo[l][r]==1; } public boolean helper(String s,List<String> wordDict) { for(String

Leetcode之动态规划(DP)专题-714. 买卖股票的最佳时机含手续费(Best Time to Buy and Sell Stock with Transaction Fee)

匿名 (未验证) 提交于 2019-12-02 23:59:01
Leetcode之动态规划(DP)专题-714. 买卖股票的最佳时机含手续费(Best Time to Buy and Sell Stock with Transaction Fee) 给定一个整数数组 prices ,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。 你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。 返回获得利润的最大值。 示例 1: 输入: prices = [ 1 , 3 , 2 , 8 , 4 , 9 ], fee = 2 输出: 8 解释: 能够达到的最大利润: 在此处买入 prices [ 0 ] = 1 在此处卖出 prices [ 3 ] = 8 在此处买入 prices [ 4 ] = 4 在此处卖出 prices [ 5 ] = 9 总利润: (( 8 - 1 ) - 2 ) + (( 9 - 4 ) - 2 ) = 8. 注意: 0 < prices.length <= 50000 . 0 < prices[i] < 50000 . 0 <= fee < 50000 . DP含义: dp[i][0]表示第i天没 有股票时的最大利润,没有股票的原因可能是: 第i-1天就没有,第i天没有买入 第i-1天有股票,第i天把它卖了 dp[i]