kuangbin专题区间dp

区间DP

kuangbin专题区间dp,应该每道题都有了,小标题名字都是自己看着方便瞎起的。

卡特兰分法
多边形切割

ZOJ-3537
将多边形可按卡特兰数分法分割,以 i , j i,j i,j为顶点分割线有代价 f ( i , j ) f(i,j) f(i,j),求最小分法。

初始化 d p [ i ] [ i + 1 ] = c o s t [ i ] [ i + 1 ] = 0 dp[i][i+1]=cost[i][i+1]=0 dp[i][i+1]=cost[i][i+1]=0记忆化搜索即可。 O ( n 3 ) O(n^3) O(n3)

ll co(int i, int j){
	if(c[i][j]!=-1) return c[i][j];
	node a=h[i], b=h[j];
	return c[i][j]=Abs(a.x+b.x)*Abs(a.y+b.y)%m;
}
ll sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	ll res=inf;
	for(int k=i+1; k<j; k++){
		res=min(res, sol(i, k)+co(i, k)+sol(k, j)+co(k, j));
	}
	return dp[i][j]=res;
}
栈的进出

LightOJ-1422
给定每个时刻栈顶元素,求最小输入数据个数。

初始化 d p [ i ] [ i ] = 1 dp[i][i]=1 dp[i][i]=1 d p [ i + 1 ] [ i ] = 0 dp[i+1][i]=0 dp[i+1][i]=0可不写, d p [ i ] [ j ] = d p [ i ] [ j − 1 ] dp[i][j]=dp[i][j-1] dp[i][j]=dp[i][j1](选新的), d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j − 1 ] ,   { k ∣ a [ k ] = = a [ j ]   & &   i < = k < j } dp[i][j]=dp[i][k]+dp[k+1][j-1],\ \{k\mid a[k]==a[j]\ \&\&\ i<=k<j\} dp[i][j]=dp[i][k]+dp[k+1][j1], {ka[k]==a[j] && i<=k<j}(用以前的)。 O ( n 3 ) O(n^3) O(n3)

int sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	int res=sol(i, j-1)+1;
	if(a[j]==a[j-1]) res=min(res, dp[i][j-1]);
	for(int k=i; k<=j-2; k++){
		if(a[k]==a[j])
		res=min(res, sol(i, k)+sol(k+1, j-1));
	}
	return dp[i][j]=res;
}
括号配对

POJ-2955
CF-149D
s应为: ab 或 (a)。

不连续:即())()=4。

其中一种方法:

d p [ i ] [ j ] = d p [ i + 1 ] [ j − 1 ] + 2 ,   { a [ i ] = = ′ ( ′ & & a [ j ] = = ′ ) ′ } dp[i][j]=dp[i+1][j-1]+2,\ \{a[i]=='(' \&\& a[j]==')'\} dp[i][j]=dp[i+1][j1]+2, {a[i]==(&&a[j]==)} d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k + 1 ] [ j ] ,   { k ∣ i < = k , k + 1 < = j } dp[i][j]=dp[i][k]+dp[k+1][j],\ \{k\mid i<=k, k+1<=j\} dp[i][j]=dp[i][k]+dp[k+1][j], {ki<=k,k+1<=j}

取最小值即可。

连续:即())()=2。(没有题目提交过,不保证正确),用栈或者:

d p [ i ] [ j ] = 1 ,   { a [ i ] = = ′ ( ′ & & a [ j ] = = ′ ) ′ & & d p [ i + 1 ] [ j − 1 ] } dp[i][j]=1,\ \{a[i]=='(' \&\& a[j]==')' \&\& dp[i+1][j-1]\} dp[i][j]=1, {a[i]==(&&a[j]==)&&dp[i+1][j1]} d p [ i ] [ j ] = 1 ,   { d p [ i ] [ k ] & & d p [ k + 1 ] [ j ] & & i < k ,   k + 1 < j } dp[i][j]=1,\ \{dp[i][k]\&\&dp[k+1][j]\&\&i<k,\ k+1<j\} dp[i][j]=1, {dp[i][k]&&dp[k+1][j]&&i<k, k+1<j}

数列取数
数列两端取数

d p [ i ] [ j ] = F ( f ( d p [ i + 1 ] [ j ] ,   a [ i ] ) ,   f ( d p [ i ] [ j − 1 ] ,   a [ j ] ) ) dp[i][j]=F(f(dp[i+1][j],\ a[i]),\ f(dp[i][j-1],\ a[j])) dp[i][j]=F(f(dp[i+1][j], a[i]), f(dp[i][j1], a[j]))

//eg:score=cnt(选取顺序)*a[i],求maxn(score),不用初始化
for(int i=n; i>=1; i--){
	for(int j=i; j<=n; j++){
		dp[i][j]=max(dp[i+1][j]+a[i]*(n-(j-i)),
			dp[i][j-1]+a[j]*(n-(j-i)));
	}
}
数列中间取数

POJ-1651
1,n最后取, d p [ i ] [ j ] dp[i][j] dp[i][j]为去完 i , j i,j i,j中间的数最小或最大代价。

d p [ i ] [ j ] = d p [ i ] [ k ] + d p [ k ] [ j ] + f [ i , k , j ] dp[i][j]=dp[i][k]+dp[k][j]+f[i,k,j] dp[i][j]=dp[i][k]+dp[k][j]+f[i,k,j],最后取 k k k

int sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i+1==j) return dp[i][j]=0;
	int res=(int)inf;
	for(int k=i+1; k<j; k++){
		res=min(res, sol(i, k)+sol(k, j)+a[i]*a[k]*a[j]);
	}
	return dp[i][j]=res;
}
可入栈的取数

HDU-4283
已有顺序1到n,和代价a[i],个人代价为前面出场人数乘a[i],有一空栈可用于调整顺序。

//错误:没有考虑中间出场 
int solwr(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i>=j) return dp[i][j]=0;
	return dp[i][j]=min(s[j]-s[i], a[i]*(j-(i+1)+1))+solwr(i+1, j);
}
int sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i>=j) return dp[i][j]=0;
	int res=s[j]-s[i]+sol(i+1, j);
	for(int k=i+1; k<=j; k++){
		res=min(res, sol(i+1, k)+a[i]*(k-(i+1)+1)+
			(s[j]-s[k])*(k-i+1)+sol(k+1, j));
	}
	return dp[i][j]=res;
}
段覆盖
覆盖空字符串

每次可将 a a a的一连续段改为某一字符,求将空字符串 a a a修改为 s s s的最小修改次数。

没有重复字符: d p [ j ] [ i ] = d p [ j + 1 ] [ i ] + 1 = d p [ j ] [ k ] + d p [ k + 1 ] [ i ] dp[j][i]=dp[j+1][i]+1=dp[j][k]+dp[k+1][i] dp[j][i]=dp[j+1][i]+1=dp[j][k]+dp[k+1][i]

显然,必须采取先覆盖两端边界为最优解,故:

s [ j ] = = s [ i ] :   d p [ j ] [ i ] = d p [ j + 1 ] [ i ] = d p [ j ] [ i − 1 ] s[j]==s[i]:\ dp[j][i]=dp[j+1][i]=dp[j][i-1] s[j]==s[i]: dp[j][i]=dp[j+1][i]=dp[j][i1]

这里固定 i i i即可计算。

memset(dp, 0, sizeof(dp));
for(int i=0; i<n; i++){
	for(int j=i; j>=0; j--){
		dp[j][i]=dp[j+1][i]+1;
		for(int k=j+1; k<=i; k++){
			if(s[j]==s[k])
			dp[j][i]=min(dp[j+1][k]+dp[k+1][i], dp[j][i]);
		}
	}
}
覆盖非空字符串

HDU-2476
先计算空字符串, S [ i ] = m i n ( d p [ 0 ] [ i ] ,   S [ i − 1 ] ) + 1 S[i]=min(dp[0][i],\ S[i-1])+1 S[i]=min(dp[0][i], S[i1])+1

a [ k ] = = s [ k ] :   S [ i ] = m i n ( S [ i ] ,   S [ k − 1 ] + d p [ k + 1 ] [ i ] ) a[k]==s[k]:\ S[i]=min(S[i],\ S[k-1]+dp[k+1][i]) a[k]==s[k]: S[i]=min(S[i], S[k1]+dp[k+1][i])

S[0]=(a[0]==s[0]?0:1);
for(int i=1; i<n; i++){
	S[i]=min(dp[0][i], S[i-1]+1);
	for(int k=0; k<=i; k++){
		if(a[k]==s[k]) S[i]=min(S[i], S[k-1]+dp[k+1][i]);
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值