首页 > 其他分享 >区间dp

区间dp

时间:2024-02-17 18:44:20浏览次数:25  
标签:25 输出 int 样例 1111 区间 dp

Ø合并:意思就是将两个或多个部分进行整合,当然也可以反过来,也就是是将一个问题进行分解成两个或
多个部分。
Ø特征:能将问题分解成为两两合并的形式
Ø求解:对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,最后将左右两个部分的最优值
进行合并得到原问题的最优值。有点类似分治算法的解题思想。
Ø典型试题:整数划分,凸多边形划分、石子合并、多边形合并、能量项链等。

例题1:整数划分:
题目描述
如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大。N、M从键盘输入,输出最大值及一种划分方式。
输入格式
第一行一个正整数T(T<=10000),表示有T组数据。
接下来T行每行两个正整数N,M。
输出格式
对于每组数据
第一行输出最大值。
第二行输出划分方案,将N按顺序分成M个数输出,两个数之间用空格格开。
样例
样例输入
1
199 2
样例输出
171
19 9

解:这题的dp部分事实上还是很好理解的,就是这个预处理稍微有点麻烦,不过还好数据范围够小,直接开一个二维数组就行,还有就是因为有多组数据,所以别忘了memset。看代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
char s[25];
long long a[25][25],dp[25][25];//20位会炸int,所以记得开long long,还有个事就是这题还有一个进阶版叫乘积最大,要用高精,所以为什么整理这道不用说了吧。
int vis[25][25];
//还是熟悉的递归输出
void p(int x,int y){
	if(x==0)return;
	p(vis[x][y],y-1);
	for(int i=vis[x][y]+1;i<=x;i++){
		cout<<s[i];
	}
	cout<<" ";
	return;
}
int main(){
    int T;
    int m;
    cin>>T;
    while(T--){
        scanf("%s %d",s+1,&m);//下标从1开始存,额--,习惯问题
        int len=strlen(s+1);
        //重新初始化
        memset(vis,0,sizeof(vis));
        memset(a,0,sizeof(a));
        memset(dp,0,sizeof(dp));
        //预处理a[i][j]表示第i位到第j位数的实际大小
        for(int i=1;i<=len;i++){             
			long long ink=0;
            for(int j=i;j<=len;j++){
                ink=ink*10+s[j]-'0';
               	a[i][j]=ink;
            }
    	}
        dp[0][0]=1; 
        //dp[i][j]表示前i个数分成j组的最大乘积,下面就基本上是板子了
        for(int i=1;i<=len;i++){
            int w=min(i,m);
            for(int j=1;j<=w;j++){
                for(int k=1;k<=i;k++){
                	if(dp[i][j]<dp[k-1][j-1]*a[k][i]){
                		dp[i][j]=max(dp[i][j],dp[k-1][j-1]*a[k][i]);
      					vis[i][j]=k-1;
					}
                    
                }
            }
        } 
        printf("%lld\n",dp[len][m]);
        if(m==len){
        	for(int i=1;i<=m;i++){
        		cout<<s[i]<<' ';
			}
		} 
        else p(len,m);
        cout<<endl; 
    }
    return 0;
}
例题2:石子合并: 在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆最大得分.

输入格式
数据的第1行试正整数N,1≤N≤2000,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式
输出共1行,最大得分

样例
样例输入
4
4 4 5 9
样例输出
54

解:这题怎么说吧,虽说是道板子,但是你注意它的范围2000,如果正常跑板子肯定会T,但如果说数据小一点的话,跑板子完全没有问题,数据小时代码如下(板子不多解释,还有环时存双链):

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int f[1111][1111],g[1111][1111],a[1111],s[1111],n;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i+n]=a[i];
	}
	for(int i=1;i<=n*2;i++){
		s[i]=s[i-1]+a[i];
	}
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<=n*2;i++)f[i][i]=0;
	for(int len=2;len<=n;len++){
		for(int i=1;i+len-1<=n*2;i++){
			int j=i+len-1;
			for(int k=i;k<j;k++){
				f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
				g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+s[j]-s[i-1]);
			}
		}
	}
	int ans1=11111111,ans2=0;
	for(int i=1;i<=2*n;i++){
		ans1=min(ans1,f[i][n+i-1]);
		ans2=max(ans2,g[i][n+i-1]);
	} 
	cout<<ans1<<endl<<ans2;
	return 0;
} 
数据大时,动用一个小优化:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int f[5111][5111],a[5111],s[5111],n;

int main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i+n]=a[i];
	}
	for(int i=1;i<=n*2;i++){
		s[i]=s[i-1]+a[i];
	}
	for(int i=1;i<=n*2;i++)f[i][i]=0;
	for(int len=1;len<=n;len++){
		for(int i=1;i+len<=n*2;i++){
			int j=i+len;
			f[i][j]=max(f[i+1][j],f[i][j-1])+s[j]-s[i-1];
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i][n+i-1]);
	}
	cout<<ans;
	return 0;
} 



例3:图多边形的三角划分:
给定一具有N个顶点(从1到N编号)的凸多边形,每个顶点的权均已知。问如何把这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权的乘积之和最小?image

输入格式
第一行 顶点数N(N<50)。 第二行 N个顶点(从1到N)的权值,权值为小于32768的整数。

输出格式
第一行为各三角形顶点的权的乘积之和最小值。

样例
样例输入
5
121 122 123 245 231
样例输出
12214884

解:一眼看去事实上并没有什么思路,但想一下就会发现,这其实是一道区间dp,看代码:

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int a,b,c,d,n,m;
long long x[1111],f[211][211];
//f[i][j]表示已i-j为边的多边形的最小乘积之和
int main(){
    cin>>n;
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;i++){
    	cin>>x[i];
    	x[i+n]=x[i];
    	f[i][i]=f[i][i+1]=0;
	}
	for(int len=1;len<=n;len++){
		for(int i=1;i+len<=n;i++){
			int j=i+len;
			for(int k=i+1;k<j;k++){
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]+x[i]*x[k]*x[j]);
			}
		}
	}
	cout<<f[1][n];
    return 0;
}

标签:25,输出,int,样例,1111,区间,dp
From: https://www.cnblogs.com/houburzyx/p/18018212

相关文章

  • 区间dp
    合并类动态规划合并:意思就是将两个或多个部分进行整合,当然也可以反过来,也就是是将一个问题进行分解成两个或多个部分。特征:能将问题分解成为两两合并的形式求解:对整个问题设最优值,枚举合并点,将问题分解成为左右两个部分,最后将左右两个部分的最优值进行合并得到原问题的最优......
  • 区间dp
    1.合并石子(1)排成一列的石子这个与合并果子唯一的不同就是只能合并相邻的石子,所以贪不得(怎么所有类型的动规要先上来搞贪心,有点diss贪心的感觉)f[i][j]表示i到j间合并的最大/最小得分;核心for(intlen=2;len<=n;len++){//表示长度2到len时的最大 for(inti=1;i+len-1<=n;i++)......
  • 背包dp
    01背包描述:有n个物品,每个物品只有一件,第i个物品体积为vi,价格为ci。现在有一个体积为V的背包,请你从n件物品里选出若干件放进背包里,使得背包里的物品价值最大。思路:01背包的特点是:每种物品只有一件,可以选择放或不放。我们可以根据此特点进行动态规划(DP),设f[i][j]表示前i件物品放......
  • 动态规划-DP 完整版
    动态规划学完了五大基础dp做个简单总结dp特征动态规划问题首要是找到做题的目的是求最大/小值还是其他;其二要确定问题的状态转移方程这是关键;第三为dp数组找到边界、最后检查是否需要结合其他相关知识如树dfs等;别忘了检查多测输入数组变量置零等易错点。......
  • 线性dp
    线性动态规划:不用多说,主要应用于求上升子序列,下降子序列等直接看例题:样例输入:13791638243718441921226315样例输出:max=879161819212263解:#include<bits/stdc++.h>usingnamespacestd;constintMAX=1050;intn,ans;intf[MAX],......
  • DP总结
    DP总结1.背包DP-0/1背包-完全背包-多重背包-分组背包-依赖背包-二维背包-树形背包DP0/1背包朴素版点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintmaxn=1010;//f[i][j]表示前i个物品,体积不超过j时的最大价值//不选第i个物品时,f[i][j]......
  • 回顾复习之线性DP
    概念具有线性阶段划分的动态规划算法叫作线性动态规划(简称线性DP)。若状态包含多个维度,则每个维度都是线性划分的阶段,也属于线性DP,如下图所示:如果状态包含多个维度,但是每个维度上都是线性划分的阶段,也属于线性DP。比如背包问题、区间DP、数位DP等都属于线性DP。例题求最......
  • 动态规划--一维dp和二维dp
    在解决背包问题时,使用一维动态规划数组和二维动态规划数组都是常见的方法,选择哪种方式取决于问题的特点和解法的需要。使用一维DP数组的情况:状态转移方程只涉及到上一行的元素:当状态转移方程只涉及到上一行的元素时,可以使用一维DP数组。这样能够降低空间复杂度,使算法更为简......
  • DP总结
    DP(动态规划)简介动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。由于动态规划并不是某种具体的算法,而是一种解决特定问题的方法,因此它会出现在各式各样的数据结构中,与之相关的题目种类也更为繁杂。DP基础1.必要前提需要满足三个条件:最优子......
  • 树形dp
    树形dp模型:给定一颗有n个节点的树,任选一个节点为根节点,从而定义出每个节点的深度和每棵子树的根。基本思路:1.建树2.列出动态转移方程典型例题:没有上司的舞会#include<bits/stdc++.h>usingnamespacestd;intn,l,k,ans;vector<int>son[6600];intf[6600][2],v[6600],r......