洛谷题单【动态规划3】区间与环形动态规划

P1880 [NOI1995] 石子合并

思路

对于区间dp,引用OI Wiki里的介绍:在这里插入图片描述

对于环形的情况,我们讲这条链延申两倍,变成2*n堆,其中第i堆和第i+n堆相同。用动态规划求解后,求出 d p [ 1 ] [ n ] , d p [ 2 ] [ n + 1 ] . . . d p [ n − 1 ] [ 2 n − 2 ] dp[1][n],dp[2][n+1]...dp[n-1][2n-2] dp[1][n],dp[2][n+1]...dp[n1][2n2]的最大/最小值。
需要注意的是dp2[i][j]需要赋初值为一个大整数,但不能将开始循环的区间长度设置为1,要设置为2,否则dp2[i][j]就全部为这个大整数的值了 = =

P1063 [NOIP2006 提高组] 能量项链 和这道题类似

实现

#include <bits/stdc++.h>
#define maxn 220
using namespace std;
typedef long long ll;
int n;
int s[maxn];
int a[maxn];
int dp1[maxn][maxn];
int dp2[maxn][maxn];
int main()
{
	int t;
	scanf("%d\n",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i+n] = a[i];
	}
	for(int i=1;i<=2*n;i++)
	{
		s[i] = s[i-1]+a[i];
	}
	for(int len=2;len<=n;len++)  //len从2开始
	{
		for(int l=1;l<=(2*n-len);l++)
		{
			int r = l+len-1;
			dp2[l][r] = 10e8;
			for(int k = l;k<r && k<=2*n-1;k++)
			{
				dp1[l][r] = max(dp1[l][r],dp1[l][k]+dp1[k+1][r]+s[r]-s[l-1]);
				dp2[l][r] = min(dp2[l][r],dp2[l][k]+dp2[k+1][r]+s[r]-s[l-1]);
			}
		}
	}
	int max1 = 0;
	int min1 = 10e8;
	for(int i=1;i<n;i++)
	max1 = max(max1,dp1[i][i+n-1]);
	
	for(int i=1;i<n;i++)
	min1 = min(min1,dp2[i][i+n-1]);
		
	cout<<min1<<endl;
	cout<<max1;
	return 0;
}

P3205 [HNOI2010]合唱队

思路

d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j]记录区间 [ i , j ] [i,j] [i,j]且最后一个插入的数为 a [ i ] a[i] a[i]的排列方法总数, d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j]记录区间 [ i , j ] [i,j] [i,j]且最后一个插入的数为 a [ j ] a[j] a[j]的排列方法总数。

实现

#include <bits/stdc++.h>
#define maxn 1010
using namespace std;
typedef long long ll;
int n;
int mod = 19650827;

int s[maxn];
int a[maxn];
int dp1[maxn][maxn];
int dp2[maxn][maxn];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		dp1[i][i] = 1;
		//dp2[i][i] = 1;
	}
	for(int len=2;len<=n;len++)
	{
		for(int l = 1;l<=(n-len)+1;l++)
		{
			int r = l+len-1;
			dp1[l][r] = ( (a[l]<a[l+1]) * dp1[l+1][r] + (a[l]<a[r]) * dp2[l+1][r] )%mod;
			dp2[l][r] = ( (a[r]>a[l]) * dp1[l][r-1]+(a[r]>a[r-1]) * dp2[l][r-1] )%mod;
		}
	}
	cout<<(dp1[1][n]+dp2[1][n])%mod;
	return 0;
}

P3146 [USACO16OPEN]248 G

思路

和石子合并类似

实现

#include <bits/stdc++.h>
#define maxn 300
using namespace std;
typedef long long ll;
int n;
int s[maxn];
int a[maxn];
int dp[maxn][maxn];
int ans;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) 
	{	
		scanf("%d",&a[i]);
		dp[i][i] = a[i];
	}
	for(int len=2;len<=n;len++)
	{
		for(int l=1;l<=n-len+1;l++)
		{
			int r = l+len-1;
			for(int k=l;k<r;k++)
			{
				if(dp[l][k]==dp[k+1][r] && dp[l][k]!=0)
				{
					dp[l][r] = max(dp[l][r],dp[l][k]+1);
					ans = max(ans,dp[l][r]);
				}
			}
		}
	}
	cout<<ans<<endl;
	return 0;
}

P4170 [CQOI2007]涂色

思路

对于一个已经涂好的区间 [ i , j ] [i,j] [i,j],如果 i i i j j j颜色相同,则 d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j] = min(dp[i-1][j],dp[i][j-1]) dp[i][j]=min(dp[i1][j],dp[i][j1])(只要上次再多涂一块就行)
i i i j j j颜色不同时需要枚举分割点

实现

#include <bits/stdc++.h>
#define maxn 300
using namespace std;
typedef long long ll;
int n;
string t;
char a[maxn];
int dp[maxn][maxn];
int main()
{
	cin>>t;
	n = t.size();
	for(int i=1;i<=n;i++) 
	{	
		a[i] = t[i-1];
		dp[i][i] = 1;
	}
	for(int len=2;len<=n;len++)
	{
		for(int l=1;l<=n-len+1;l++)
		{
			int r = l+len-1;
			dp[l][r] = 10e8;
			if(a[r] == a[l]) 
			{
				dp[l][r] = min(dp[l][r-1],dp[l+1][r]);
				continue;
			}
			for(int k=l;k<r;k++)
			{
				dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
			}
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

CF607B Zuma

思路

思路来自:洛谷题解区题解

读入n的时候scanf(“%d\n”,&n);可以AC
scanf(“%d”,&n);的话第45个点会wa,不理解hhh

实现

#include <bits/stdc++.h>
#define maxn 505
using namespace std;
typedef long long ll;
int n;
int a[maxn];
int dp[maxn][maxn];
int ans;
int main()
{
	memset(dp,127,sizeof(dp));
	scanf("%d\n",&n);
	for(int i=1;i<=n;i++) 
	{	
		scanf("%d",&a[i]);
		dp[i][i] = 1;
	}
	for(int i=1;i<n;i++)
	{
		if(a[i]==a[i+1]) dp[i][i+1] = 1;
		else dp[i][i+1] = 2;
	}
	for(int len=3;len<=n;len++)
	{
		for(int l=1;l<=n-len+1;l++)
		{
			int r = l+len-1;
			if(a[r] == a[l]) 
			{
				dp[l][r] = dp[l+1][r-1];
			}
			for(int k=l;k<r;k++)
			{
				dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
			}
		}
	}
	cout<<dp[1][n]<<endl;
	return 0;
}

P4933 大师

实际上是求:在n个数字中选一些数使其构成等差数列(一个 or 两个数字也算等差数列)问一共有多少种选法?
但是自己没做出来 = =

参考题解

欢迎指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值