区间dp

区间 \(dp\) 一般还是有很明显的提示,时间复杂度 \(O(n^3)\),因此一般 \(n\leq 500\)。实现一般也很套路,外层循环枚举区间长度 \(len\);中层枚举始端点 \(i\),并得出末端点 \(j=i+len-1\);内层枚举 \(k,i\leq k<j\);写状态转移方程。

例题一:石子合并

思路

最简单的区间 \(dp\),以求 \(max\) 而言 \(f_{i,j}=\max(f_{i,j},f_{i,k}+f_{k+1,j})\)

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n, v[210], f1[210][210], f2[210][210], a[210], x;

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
	read(n);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	for(register int i = 1; i <= n; ++i)	v[i] = v[i-1] + a[i];
	for(register int i = n+1; i <= 2*n; ++i)	v[i] = v[i-1] + a[i-n];
	for(register int len = 2; len <= 2*n; ++len)
		for(register int i = 1; i <= 2*n-len+1; ++i){
			int j = i+len-1;
			f1[i][j] += f1[i+1][j];
			f2[i][j] += f2[i+1][j];
			for(register int k = i; k <= j-1; k++){
				f1[i][j] = min(f1[i][j], f1[i][k]+f1[k+1][j]);
				f2[i][j] = max(f2[i][j], f2[i][k]+f2[k+1][j]);
			}
			f1[i][j] += v[j]-v[i-1];
			f2[i][j] += v[j]-v[i-1];
		}
	int minn = 0x3f3f3f3f, maxn = -0x3f3f3f3f;
	for(register int i = 1; i <= n; ++i){
		if(minn > f1[i][i+n-1])	minn = f1[i][i+n-1];
		if(maxn < f2[i][i+n-1])	maxn = f2[i][i+n-1];
	}
	write(minn), puts(""), write(maxn);
	return 0;
}

例题二:248 G

思路

还是一样的套路,\(f_{i,j}=\max(f_{i,j},f_{i,k}+1),f_{i,k}=f_{k+1,j}\neq0\)

\(Code\)

#include<bits/stdc++.h>
using namespace std;

int n;
int x, f[250][250];
int ans = 0;

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
	read(n);
	for(register int i = 1; i <= n; ++i)	read(x), f[i][i] = x;
	for(register int len = 2; len <= n; ++len)
		for(register int i = 1; i <= n-len+1; ++i){
			int j = i+len-1;
			for(register int k = i; k < j; ++k){
				if(f[i][k] == f[k+1][j] && f[i][k] != 0){
					f[i][j] = max(f[i][j], f[i][k]+1);
					ans = max(ans, f[i][k]+1);
				}
			}
		}
	write(ans);
	return 0;
}

例题三:涂色

思路

状态转移方程稍微麻烦一点,但是作法没有实质上的改变。

\[f_{i,j}= \begin{cases} 1,i=j\\ \min(f_{i,j-1},f_i+1,j),i\neq j,a_i=a_j\\ \min(f_{i,j},f_{i,k}+f_{k+1,j}),i\neq j,a_i\neq a_j \end{cases} \]

\(Code\)

#include<bits/stdc++.h>
using namespace std;

char a[55];
int f[55][55];

template<typename T>
inline void read(T&x){
	x = 0; char q; bool f = 1;
	while(!isdigit(q = getchar()))	if(q == '-')	f = 0;
	while(isdigit(q)){
		x = (x<<1) + (x<<3) + (q^48);
		q = getchar();
	}
	x = f?x:-x;
}

template<typename T>
inline void write(T x){
	if(x < 0){
		putchar('-');
		x = -x;
	}
	if(x > 9)	write(x/10);
	putchar(x%10^48);
}

int main(){
	memset(f, 0x3f3f3f3f, sizeof(f));
	scanf("%s", a+1);
	int n = strlen(a+1);
	for(register int i = 1; i <= n; ++i)	f[i][i] = 1;
	for(register int len = 2; len <= n; ++len)
		for(register int i = 1; i <= n-len+1; ++i){
			int j = i+len-1;
			if(a[i] == a[j]){
				f[i][j] = min(f[i][j-1], f[i+1][j]);
				continue;
			}
			for(register int k = i; k < j; ++k)	f[i][j] = min(f[i][j], f[i][k]+f[k+1][j]);
		}
	write(f[1][n]);
	return 0;
}
posted @ 2024-10-09 18:35  Zzzzzzzm  阅读(5)  评论(0)    收藏  举报