ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

dls的区间dp

2022-03-11 13:34:26  阅读:189  来源: 互联网

标签:题目 course int long dls 括号 区间 const dp


区间dp

ICPC Beijing 2017 J, Pangu and Stones

题目链接:http://oj.daimayuan.top/course/8/problem/327
题目大意:有n堆石子,每对有ai个,每次可以合并[L, R]堆石子,代价是这些石子的之和
    
f[l][r][k]:表示将l,r合并成k堆的最小代价
划分方式:根据最后一顿划分的分界线在哪里
f[l][r][1]:表示合并成一堆的代价,我们利用前面的计算的f[l][r][k]的k是否在L-R之间在进行真正的代价合并

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 110;
const LL inf = 1ll<<60;
int a[N], s[N];
LL f[N][N][N];
int main(){
	int n, L, R;
	while(scanf("%d %d %d", &n , &L, &R) == 3){

		for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
		for(int i = 1; i <= n; i ++) s[i] = s[i-1] + a[i];
		for(int i = 1; i <= n; i ++){
			for(int j = 1; j <= n; j ++){
				for(int k = 1; k <= n;  k ++){
					f[i][j][k] = inf; 
				}
			}
		}

		for(int len = 1; len <= n; len ++){
			for(int l = 1; l + len - 1 <= n; l ++){
				int r = l + len - 1;
				// 边界,一般边界写好了,下面按照思路正常进行dp就可以了
				if(len == 1) f[l][r][1] = 0;
				else{
				// 合并其实并没有发生在枚举mid那里,mid哪里只是找把l,r变成k堆的最小代价
				// 而是k这个循环的最后,才真正的将l,r合并起来
					for(int k = 2; k <= n; k ++){
						for(int mid = l; mid < r; mid ++){
							f[l][r][k] = min(f[l][r][k], f[l][mid][1] + f[mid+1][r][k-1]);
						}
						if(k >= L && k <= R) f[l][r][1] = min(f[l][r][1], f[l][r][k] + s[r] - s[l-1] );
					}
				}
			}
		}
		if(f[1][n][1] <= (1ll<<60)/2) printf("%lld\n", f[1][n][1]);
		else puts("0");
	}	
	return 0;
}

ICPC Kunming 2020 C, Cities

题目链接:http://oj.daimayuan.top/course/8/problem/327
题目大意:给定一个长度为n(<=5000)的序列,每次可以选择一段连续相等的序列把它变成另外一个数,问使该序列变成相同的最少操作次数,每个数出现的次数不超过15次

f[l][r]表示将l,r合并成相同的最小代价的话
那么我们需要枚举和a[l]相等的其他位置,如果这个位置是r的话,显然我们可以用f[l+1][r]来更新f[l][r]
如果相等的位置是在中间的话,我们可以发现一个性质,一定存在一种替换方案,使得al...ar变成al并且次数是最小的,所以现在的结果直接用f[l][p]+f[p][r]更新就可以了,我们把他们都染成a[l]这个数字

#include<bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 5010, inf = 1<< 29;
vector<int> pos[N];
int f[N][N];
int a[N];

int main(){
	int T;
	cin >> T;
	while(T--){
		int n;
		cin >> n;
		for(int i = 1; i <= n; i++){
			scanf("%d", &a[i]);
			pos[a[i]].clear();
		}
		for(int i = 1; i <= n; i++){
			pos[a[i]].push_back(i);
		}
		for(int len = 1; len <= n; len ++){
			for(int l = 1; l + len - 1 <= n; l ++){
				int r = l + len - 1;
				f[l][r] = inf;
			}
		}
		for(int len = 1; len <= n; len ++){
			for(int l = 1; l + len - 1 <= n; l ++){
				int r = l + len - 1;
				if(len == 1) f[l][r] = 0;
				else{
					f[l][r] = f[l+1][r] + 1;
					for(auto p : pos[a[l]]){
						if(p > r) break;
						if(p <= l) continue;
						if(p == r) f[l][r] = min(f[l][r], f[l+1][r]);
						else f[l][r] = min(f[l][r], f[l+1][p] + f[p][r] );
					}
				}
			}
		}
		printf("%d\n", f[1][n]);
		{
	}
	return 0;
}

CSP-S 2021,括号序列

题目链接:http://oj.daimayuan.top/course/8/problem/329
普通的括号序列如何做:只需要保证任意位置前面左括号的数量大于等于右括号的数量,并且最后的左右括号的数量相等
f[i][j]表示前i个位置,左括号的个数-右括号的个数
改进的括号序列:
f[l][r]表示合法的方案数量:

标签:题目,course,int,long,dls,括号,区间,const,dp
来源: https://www.cnblogs.com/njw1123/p/15993315.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有