区间DP

【引言】:

区间DP,比较板,一般情况下就是直接枚举左端点,之后枚举右端点,在左端点和右端点之间枚举断点,用于更新这一段区间

1.【题目】:P4170 [CQOI2007]涂色

【状态设计】:

由于很明显是区间DP,也就没必要分析具体怎么分析了,状态也就是\(f_{i,j}\)表示讲\(s_i\)\(s_j\)这个区间染色的最优解;

【状态转移】:

考虑\(f_{i,j}\)这个区间;

如果 \(i==j\)的时候,我们发现,就是需要染色一次即可 ,即为 \(f_{i,i}=1\)初始化的时候搞一下就\(OK\)

如果 \(i!=j\)&&\(s_i==s_j\)也就是说,在上一层染色的时候直接给多染色一格,就可以了,即为 \(f_{i,j}=min(f_{i,j-1} , f_{i+1,j})\)即可

如果 \(i!=j\)&&\(s_i!=s_j\) ,这个时候就像上面所说的,枚举断点,合并,即为 \(f_{i,j} = \min\limits_{l≤k leq} f_{i,k} + f_{k+1,j})\)也就是两段区间都需要各自染色,合并取得最小值

【code】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
const int maxn=1e6;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch == '-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}
int n;
int f[60][60];
char s[maxn];
int main()
{
	scanf("%s",s+1);
	memset(f,0x3f,sizeof(f));
	int n=strlen(s+1);
	for(int i=1;i<=n;i++)
	{
		f[i][i]=1;
	}
	for(int len=1;len<=n;len++) //区间长度 
	{
		for(int l=1,r=l+len;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]);
				}
			}
		}
	} 
	printf("%d",f[1][n]);
	return 0;
}

【题目】P1880 [NOI1995]石子合并

【引言】:

MD,老和我作对,刚打完码,然后发现圆形操场,然后发现,这个和能量项链十分的相似,同时,写完这个博客就不写能量项链了

【状态设计】:

\(f_{i,j}\)还是表示合并从\(i\)\(j\)这个区间的极值(最大值和最小值分别设一个就好)。

【状态转移】:

以最大值为例,\(f_{i,j}=\max\limits _{l≤k\leq r} f_{l,k} + f_{k+1,r} + sum_{r} - sum_{l-1}\)

因为合并的时候需要加上前面的值,直接前缀和搞一下就好,最小值也是一样的,本来这个题是要用四边形不等式,但是由于是个板子题,正常区间\(DP\)也可以做,还有注意的是,形成的是一个环,那既然是环,这里就有个小技巧,

就是\(sum_{n+i} = sum_{i}\)这个就说绕了一圈,绕回来了,手动模拟一下也能出来这个结果,推荐手动模拟一下(\(yy\)一下也可,毕竟\(so,easy\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define int long long
using namespace std;
const int maxn=500;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){ if(ch == '-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}
int n;
int a[maxn];
int sum[maxn];
int f1[maxn][maxn];//最大值 
int f2[maxn][maxn];//最小值 
int ans1,ans2;
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		sum[i] = read();
		sum[n+i] = sum[i];
	}
	for(int i=1;i<n+n;i++)
	{
		sum[i] += sum[i-1];
		//printf("%d\n",sum[i]);
	}
	for(int i=2;i<=n;i++)
	{
		for(int l=1;l + i -1 < n+n; l++)
		{
			int r= l + i -1;
			f2[l][r] = 999999999;
			f1[l][r] = -999999999;
			for(int k=l;k<r;k++)
			{
				f1[l][r] = max(f1[l][k] + f1[k+1][r] + (sum[r] - sum[l-1]), f1[l][r]);
				//printf("%d\n",f1[l][r]);
				f2[l][r] = min(f2[l][k] + f2[k+1][r] + (sum[r] - sum[l-1]), f2[l][r]);
				//printf("%d\n",f2[l][r]);
			}
		}
	}
	ans2 = 999999999; // 0x3f 就真给我搞个 63了 
	for(int i=1;i<=n;i++)
	{
		ans1 = max(ans1,f1[i][n+i-1]);//把i写成n,WA了半年 
		//printf("%d\n",ans2);
		ans2 = min(ans2,f2[i][n+i-1]);
	}
	printf("%d\n",ans2);
	printf("%d",ans1);
	return 0;
}

3.P433大师

很明显,区间DP,求公差数列个数

很明显,区间DP,求公差数列个数

状态设计: \(f_{i,k}\)表示以\(i\)结尾,公差为\(k\)的公差数列个数,反正我当时是没想出来

状态转移:\(f_{i,k}=f_{j,k}+f_{i,k}+1\)其中 \(1\le j \leq i-1\),

对于一个区间 \([l,r]\)中有等差数列,设公差为\(k\),那么其公差数列中任意挑选一个数设为\(x\),那么表示为\(f_{x,k}\),在这个区间中又有一个数\(y\),如果 \(y-x=k\),自然\(y\)也是属于等差数列中的,但是我们总不能在计算吧,所以我们就DP,也算递推 可得 \(f_{y,k}=f_{y,k}+f_{x,k}+1\),两个数也算一个等差数列 所以加个1;

【code】

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define int long long
using namespace std;
const int maxn=20008;
const int mod=998244353;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int num[maxn];
int f[1003][2*maxn];
int n;
int Max=-1;
int ans; 
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		num[i]=read();
		Max=max(Max,abs(num[i]));
	}	
	for(int r=2;r<=n;r++)
	{
		for(int l=1;l<r;l++)
		{	
			f[r][ num[r]-num[l]+Max ] = (f[r][ num[r]-num[l]+Max ]+f[l][ num[r]-num[l]+Max ]+1)%mod;
			ans=(ans+f[l][num[r]-num[l]+Max]+1)%mod;
		}
	}
	ans+=n;
	cout<<ans%mod<<endl;
	return 0;
}
posted @ 2020-11-29 21:22  SkyFairy  阅读(92)  评论(0编辑  收藏  举报