区间DP例题汇总

模板

首先预处理出所有的f[i][i],然后去解决f[l][r]

for(int d=1;d<=n;d++) {
	for(int i=1,j=d+1;j<=n;i++,j++) {
	}
}

洛谷P4170

题目链接

https://www.luogu.org/problemnew/show/P4170

题目

给定一个初始为空白的木板,一次可以给连续的一段染色,问最少染多少次,可将木板染成指定颜色。

题解

设f[l,r]表示将[l,r]区间涂成正确颜色需要的次数

  1. 当l==r时,子串明显只需要涂色一次,于是f[l][r]=1。
  2. 当l!=r且s[l]==s[r]时,可以想到只需要在首次涂色时多涂一格即可,于是f[l][r]=min(f[l][r-1],f[l+1][r])
  3. 当l!=r且s[l]!=s[r]时,我们需要考虑将子串断成两部分来涂色,于是需要枚举子串的断点,设断点为k,那么f[l][r]=min(f[l][r],f[l][k]+f[k+1][r])

代码

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 200005;
const int N = 105;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

string s;
int f[55][55], n;

int main() {
	ios::sync_with_stdio(false);
	cin >> s;
	n = s.length();
	memset(f,0x3f,sizeof(f));
	for(int i=0;i<n;i++) f[i][i] = 1;
	for(int d=1;d<=n;d++) {
		for(int l=0,r=d;r<n;l++,r++) {
			if(s[l]==s[r]) f[l][r] = min(f[l+1][r],f[l][r-1]);
			else for(int k=l;k<r;k++) f[l][r] = min(f[l][r],f[l][k]+f[k+1][r]);
		}
	}
	cout<<f[0][n-1];
	return 0;
}

洛谷P1430

题目链接

https://www.luogu.org/problemnew/show/P1430

题目

给定一个长为n的整数序列(n<=1000),由A和B轮流取数(A先取)。每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取。所有数都被取走后,两人分别统计所取数的和作为各自的得分。假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分。

题解1

求A的最高得分,可以化作求A-B的最高得分,因为A-B=A-(sum-A)=2A-sum,而sum又是一个定值。
令f[l][r]表示当剩余区间为[l,r]时,A-B得到的最大值。
那么就有以下三种取法:

  1. A拿走区间内所有数,f[l][r] = sum[l,r];
  2. A从左端拿, f[l][r] = sum[l,l’-1] - f[l’][r], 减去的是B后一手得到的B-A的最大值;
  3. A从右端拿,f[l][r] = sum[r’+1,r] - f[l,r’]

代码1

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
const int N = 105;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int t,f[maxn][maxn],a[maxn],n,sum[maxn];

inline int getsum(int l,int r) {return sum[r]-sum[l-1];}

int main() {
    ios::sync_with_stdio(false); 
    read(t);
    while(t--) {
        read(n);
        for(int i=1;i<=n;i++) read(a[i]);
        INIT(f);
        INIT(sum);
        for(int i=1;i<=n;i++) sum[i] = sum[i-1] + a[i];
        for(int d=1;d<=n;d++) {
            for(int i=1,j=d;j<=n;i++,j++) {
                f[i][j] = getsum(i,j);
                for(int l=i+1;l<=j;l++)
                    f[i][j] = max(f[i][j],getsum(i,l-1)-f[l][j]);
                for(int r=j-1;r>=i;r--)
                    f[i][j] = max(f[i][j],getsum(r+1,j)-f[i][r]);
            }
        }
        cout<<(f[1][n]+sum[n])/2<<endl;
    }
    return 0; 
}

优化

上述的时间复杂度为O(n^3),明显无法通过,因此需要优化。
可以将状态转移的过程优化掉,以从右边取为例,原先需要枚举 l ≤ r ′ ≤ r l\leq r&#x27; \leq r lrr,而f[l][r] = sum[r’+1,r] - f[l,r’] = sum[r]-sum[r’]-f[l][r’], sum[r]是固定的,而r’的状态是可以通过前面的递推出来的。

代码

/***    author:  zxwsbg    ***/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <algorithm>
#include <map>
#include <set>
#include <vector>
#include <cmath>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
#define eps 1e-8
#define next next_
typedef long long ll;
typedef unsigned long long ull;
const int INF = 0x7fffffff;
const int inf = 0x3f3f3f3f;
const int maxn = 1005;
const int N = 105;

inline void read(int &x) {
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

int t,f[maxn][maxn],a[maxn],n,sum[maxn],Min[maxn][maxn],p[maxn][maxn],P[maxn][maxn],Max[maxn][maxn];

inline int getsum(int l,int r) {return sum[r]-sum[l-1];}

int main() {
	ios::sync_with_stdio(false); 
	read(t);
	while(t--) {
		read(n);
		for(int i=1;i<=n;i++) read(a[i]);
		for(int i=1;i<=n;i++) sum[i] = sum[i-1] + a[i];
		for(int i=1;i<=n;i++) {
			f[i][i] = a[i];
			Min[i][i] = sum[i] + f[i][i];
			Max[i][i] = sum[i-1] - f[i][i];
		}
		for(int d=1;d<=n;d++) {
			for(int i=1,j=d+1;j<=n;i++,j++) {
				f[i][j] = sum[j] - sum[i-1];
				f[i][j] = max(f[i][j],Max[i+1][j]-sum[i-1]);
				f[i][j] = max(f[i][j],sum[j]-Min[i][j-1]);
				Min[i][j] = min(Min[i][j-1],sum[j] + f[i][j]);
				Max[i][j] = max(Max[i+1][j],sum[i-1] - f[i][j]);
			}
		}
		cout<<(f[1][n]+sum[n])/2<<endl;
	}
	return 0; 
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

总想玩世不恭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值