其他分享
首页 > 其他分享> > dls的区间dp

dls的区间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