区间DP

区间DP

区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。

例题

石子合并 洛谷1880

#include<bits/stdc++.h>
using namespace std;
int n,i,j,k,l,ma,mi,a[205],b[205],f[205][205],g[205][205];
int main()
{
	cin>>n;
	for (i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=b[i-1]+a[i];
	}
	for (i=n+1;i<=2*n;i++)
		b[i]=b[i-1]+a[i-n];
	mi=INT_MAX;
	for (l=1;l<=n;l++)
	{
		memset(g,64,sizeof(g));
		for (i=1;i<=n*2;i++)
			g[i][i]=0;
		memset(f,0,sizeof(f));
		for (i=2;i<=n;i++)
			for (j=l;j<=n-i+l;j++)
				for (k=j;k<=j+i-2;k++)
				{
					f[j][i+j-1]=max(f[j][i+j-1],f[j][k]+f[k+1][i+j-1]+b[i+j-1]-b[j-1]);
					g[j][i+j-1]=min(g[j][i+j-1],g[j][k]+g[k+1][i+j-1]+b[i+j-1]-b[j-1]);
				}
		ma=max(ma,f[l][n+l-1]);
		mi=min(mi,g[l][n+l-1]);
	}
	cout<<mi<<'\n'<<ma<<'\n';
}

关路灯 洛谷1220

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,a[100],b[100],c[100],f[100][100][2];
int main()
{
	cin>>n>>m;
	for (i=1;i<=n;i++)
	{
		cin>>a[i]>>b[i];
		c[i]=c[i-1]+b[i];
	}
	memset(f,127,sizeof(f));
	f[m][m][0]=0;f[m][m][1]=0;
	for (i=2;i<=n;i++)
	{
		for (j=1;j<=n-i+1;j++)
		{
			f[j][j+i-1][0]=min(f[j+1][j+i-1][1]+(a[j+i-1]-a[j])*(c[n]-c[j+i-1]+c[j]),
				f[j+1][j+i-1][0]+(a[j+1]-a[j])*(c[j]+c[n]-c[j+i-1]));
			f[j][j+i-1][1]=min(f[j][j+i-2][0]+(a[j+i-1]-a[j])*(c[n]-c[j+i-2]+c[j-1]),
				f[j][j+i-2][1]+(a[j+i-1]-a[j+i-2])*(c[n]-c[j+i-2]+c[j-1]));
		}
	}
	cout<<min(f[1][n][0],f[1][n][1]);
}

洛谷 3147

Bessie喜欢在手机上下游戏玩(……),然而她蹄子太大,很难在小小的手机屏幕上面操作。

她被她最近玩的一款游戏迷住了,游戏一开始有n个正整数,(2<=n<=262144),范围在1-40。在一步中,贝西可以选相邻的两个相同的数,然后合并成一个比原来的大一的数(例如两个7合并成一个8),目标是使得最大的数最大,请帮助Bessie来求最大值。
$$
f[i][j] 保存 i这个数 以 j作为开头的合并区间的结尾,若没有就是0
$$

#include<bits/stdc++.h>
using namespace std;
int n,i,x,j,s,f[60][300005];
int main()
{
	cin>>n;
	for (i=1;i<=n;i++)
	{
		cin>>x;
		f[x][i]=i+1;
	}
	for (i=2;i<=58;i++)
		for (j=1;j<=n;j++)
		{
			if (!f[i][j])
				f[i][j]=f[i-1][f[i-1][j]];
			if (f[i][j])
				s=i;
		}
	cout<<s<<'\n';
}

合唱队 洛谷3205

#include<bits/stdc++.h>
using namespace std;
int n,i,j,a[1005],f[1005][1005][2];
const int mod=19650827;
int main()
{
	cin>>n;
	for (i=1;i<=n;i++)
	{
		cin>>a[i];
		f[i][i][0]=1;
	}
	for (i=2;i<=n;i++)
		for (j=1;j<=n-i+1;j++)
		{
			if (a[j]<a[j+1])
				f[j][i+j-1][0]=(f[j][i+j-1][0]+f[j+1][i+j-1][0])%mod;
			if (a[j]<a[i+j-1])
				f[j][i+j-1][0]=(f[j][i+j-1][0]+f[j+1][i+j-1][1])%mod;
			if (a[i+j-1]>a[i+j-2])
				f[j][i+j-1][1]=(f[j][i+j-1][1]+f[j][i+j-2][1])%mod;
			if (a[i+j-1]>a[j])
				f[j][i+j-1][1]=(f[j][i+j-1][1]+f[j][i+j-2][0])%mod;
		}
	cout<<(f[1][n][0]+f[1][n][1])%mod<<'\n';
}

涂色 洛谷4170

假设你有一条长度为 $5$ 的木板,初始时没有涂过任何颜色。你希望把它的 $5$ 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 $5$ 的字符串表示这个目标:$\texttt{RGBGR}$。

每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 $\texttt{RRRRR}$,第二次涂成 $\texttt{RGGGR}$,第三次涂成 $\texttt{RGBGR}$,达到目标。

用尽量少的涂色次数达到目标。

#include<bits/stdc++.h>
using namespace std;
int n,i,j,k,f[101][101];
string s;
int main()
{
	cin>>s;
	n=s.length();
	s='.'+s;
	memset(f,127,sizeof(f));
	for (i=1;i<=n;i++)
		f[i][i]=1;
	for (i=2;i<=n;i++)
		for (j=1;j<=n-i+1;j++)
		{
			if (s[j]==s[i+j-1])
				f[j][i+j-1]=min(f[j][i+j-2],f[j+1][i+j-1]);
			for (k=j;k<=i+j-2;k++)
				f[j][i+j-1]=min(f[j][i+j-1],f[j][k]+f[k+1][i+j-1]);
		}
	cout<<f[1][n];
}

玩具取名 洛谷4290

#include<bits/stdc++.h>
using namespace std;
int W,I,N,G,i,n,j,k,l,o;
string ww[20],ii[20],nn[20],gg[20],s;
bool f[205][205][4];
int judge(char ch)
{
	if (ch=='W') return 0;
	if (ch=='I') return 1;
	if (ch=='N') return 2;
	if (ch=='G') return 3;
	return -1;
}
int main()
{
	cin>>W>>I>>N>>G;
	for (i=1;i<=W;i++)
		cin>>ww[i];
	for (i=1;i<=I;i++)
		cin>>ii[i];
	for (i=1;i<=N;i++)
		cin>>nn[i];
	for (i=1;i<=G;i++)
		cin>>gg[i];
	cin>>s;
	n=s.length();
	s='.'+s;
	for (i=1;i<=n;i++)
		f[i][i][judge(s[i])]=1;
	for (i=2;i<=n;i++)
	{
		for (j=1,k=i;k<=n;j++,k++)
			for (l=j;l<=k-1;l++)
			{
				for (o=1;o<=W;o++)
					f[j][k][0]=f[j][k][0] or f[j][l][judge(ww[o][0])] and f[l+1][k][judge(ww[o][1])];
				for (o=1;o<=I;o++)
					f[j][k][1]=f[j][k][1] or f[j][l][judge(ii[o][0])] and f[l+1][k][judge(ii[o][1])];
				for (o=1;o<=N;o++)
					f[j][k][2]=f[j][k][2] or f[j][l][judge(nn[o][0])] and f[l+1][k][judge(nn[o][1])];
				for (o=1;o<=G;o++)
					f[j][k][3]=f[j][k][3] or f[j][l][judge(gg[o][0])] and f[l+1][k][judge(gg[o][1])];
			}
	}
	if (f[1][n][0])cout<<'W';
	if (f[1][n][1])cout<<'I';
	if (f[1][n][2])cout<<'N';
	if (f[1][n][3])cout<<'G';
	if ((!f[1][n][0])and(!f[1][n][1])and(!f[1][n][2])and(!f[1][n][3]))
		cout<<"The name is wrong!";
	cout<<'\n';
}

字符串折叠 洛谷4302

#include<bits/stdc++.h>
using namespace std;
string s;int n,i,l,k,o,r,len,t,w,f[105][105];
unsigned long long a[105],b[105];
const int base=23333233;
int main()
{
	cin>>s;
	n=s.length();
	s='.'+s;
	for (i=1;i<=n;i++)
		a[i]=a[i-1]*base+s[i];
	b[0]=1;
	for (i=1;i<=n;i++)
		b[i]=b[i-1]*base;
	memset(f,127,sizeof(f));
	for (i=1;i<=n;i++)
		f[i][i]=1;
	for (i=2;i<=n;i++)
	{
		for (l=1,r=i;r<=n;l++,r++)
		{
			len=r-l+1;
			for (k=1;k<=len/2;k++)
				if (len%k==0)
				{
					for (o=2;o<=len/k;o++)
						if (a[l+o*k-1]-a[(o-1)*k+l-1]*b[k]!=a[l+(o-1)*k-1]-a[(o-2)*k+l-1]*b[k])
							break;
					if (o!=len/k+1)
						continue;
					t=len/k;w=0;
					while (t>0)
					{
						t=t/10;
						w++;
					}
					f[l][r]=min(f[l][r],f[l][l+k-1]+2+w);
				}
			for (k=l;k<r;k++)
				f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);
		}
	}
	cout<<f[1][n]<<'\n';
}

Greedy Pie Eaters P 洛谷5851

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,l,r,k,a,b,c,f[305][305],g[305][305][305];
int main()
{
	cin>>n>>m;
	for (i=1;i<=m;i++)
	{
		cin>>a>>b>>c;
		for (j=b;j<=c;j++)
			g[j][b][c]=a;
	}
	for (i=1;i<=n;i++)
		for (j=i;j>=1;j--)
			for (k=i;k<=n;k++)
			{
				if (j!=1)g[i][j-1][k]=max(g[i][j-1][k],g[i][j][k]);
				if (k!=n)g[i][j][k+1]=max(g[i][j][k+1],g[i][j][k]);
			}
	for (i=1;i<=n;i++)
		for (l=1,r=i;r<=n;l++,r++)
		{
			for (k=l;k<r;k++)
				f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]);
			for (k=l;k<=r;k++)
				f[l][r]=max(f[l][r],f[l][k-1]+f[k+1][r]+g[k][l][r]);
		}
	cout<<f[1][n]<<'\n';
}

合并饭团 洛谷4805

#include<bits/stdc++.h>
using namespace std;
int n,i,l,r,k,j,x,ma,t,w,m,f[505][505],a[505];
int main()
{
	cin>>n;
	for (i=1;i<=n;i++)
		for (j=i+1;j<=n;j++)
			f[i][j]=-1;
	for (i=1;i<=n;i++)
	{
		cin>>f[i][i];
		a[i]=a[i-1]+f[i][i];
	}
	for (i=2;i<=n;i++)
		for (l=1,r=i;r<=n;l++,r++)
			for (t=l,w=r-1;t<=w;t++)
			{
				while (a[r]-a[w]<a[t]-a[l-1])w--;
				if (w<t) break;
				if (a[r]-a[w]!=a[t]-a[l-1])
					continue;
				if ((f[l][t]!=-1)and(f[t+1][w]!=-1)and(f[w+1][r]!=-1)and(f[l][t]==f[w+1][r]))
					f[l][r]=max(f[l][r],f[l][t]+f[t+1][w]+f[w+1][r]);
			}
	for (i=1;i<=n;i++)
		for (j=i;j<=n;j++)
			ma=max(ma,f[i][j]);
	cout<<ma<<'\n';
}
posted @   ShadowAA  阅读(18)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示