区间DP 练习 2

文章介绍了三个编程问题:1)通过动态规划计算1×n地图中2048游戏的最大可能数值;2)优化涂色次数,使木板颜色满足特定目标;3)合唱队形排序,根据身高规则确定最优队列排列。每个问题都运用了动态规划的思想来解决最优化问题。
摘要由CSDN通过智能技术生成

problem 248G
describe
给定一个 1 ×n 的地图,在里面玩 2048,每次可以合并相邻两个(数值范围 1∼40),问序列中出现的最大数字的值最大是多少。注意合并后的数值并非加倍而是 +1,例如 2 与 2 合并后的数值为 3

solution
首先当两个区间相等时,才能进行区间合并得到最大值。 当区间长度为一时,区间内的最大值等于区间本身,即dp[i][i]=a[i];

code

  #include<iostream>
    #include<cstdio>
    using namespace std;

    const int N = 250;
    int f[N][N];

    int main() {
        int n, ans = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            scanf("%d", f[i] + i);
            ans = max(ans, f[i][i]);
        }

        for(int len = 2; len <= n; len++) 
            for(int l = 1; l +len -1 <= n; l++) {
                int r = l + len - 1;
                for(int k = l; k < r; k++) 
                    if(f[l][k] == f[k + 1][r] && f[l][k]) {
                        f[l][r] = max(f[l][r], f[l][k] + 1);
                        ans = max(ans, f[l][r]);
                    }
            }

        printf("%d\n", ans);		
        return 0;
    } 

problem 涂色
describe
假设你有一条长度为 5 的木板,初始时没有涂过任何颜色。你希望把它的 5 个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为 5 的字符串表示这个目标:RGBGR
每次你可以把一段连续的木板涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木板涂成 RRRRR,第二次涂RGGGR,第三次涂成 RGBGR,达到目标。

用尽量少的涂色次数达到目标。
solution
dp[i][j] 表示在[i,j]区间内的最小次数
首先,当区间len1,即ij时,次数最小为1。当len!=1,即i!=j 时,判断最左边和最右边的元素是否一致,

code

#include <iostream>
#include <cstring> 
#define INF 0x3f3f3f
using namespace std;
int N;
int f[52][52];
string a;
int main(){
	cin>>a;
	N = a.size();
	
    	memset(f,INF,sizeof(f));
	for(int i = 1;i <= N;i++){
		f[i][i] = 1;
	}
	for(int len = 2; len <= N; len++){
		for(int i = 1; i <= N - len + 1; i++){
			int head = i, tail = i + len - 1;
			if(a[head-1] == a[tail-1]){
					f[head][tail]=min(f[head + 1][tail],f[head][tail-1]);
					continue;
			}
			for(int k = head; k <= tail; k++){
				f[head][tail]=min(f[head][k] + f[k+1][tail],f[head][tail]);
			}
		}
	}
	cout<<f[1][N];
} 

problem 合唱队
describe
定合唱队一共 n 个人,第 i 个人的身高为 hi 米(1000≤hi ≤2000),并已知任何两个人的身高都不同。假定最终排出的队形是 A 个人站成一排,为了简化问题,小 A 想出了如下排队的方式:他让所有的人先按任意顺序站成一个初始队形,然后从左到右按以下原则依次将每个人插入最终棑排出的队形中:
第一个人直接插入空的当前队形中。
对从第二个人开始的每个人,如果他比前面那个人高(h 较大),那么将他插入当前队形的最右边。如果他比前面那个人矮(h 较小),那么将他插入当前队形的最左边。

当 n 个人全部插入当前队形后便获得最终排出的队形。

solution
首先大致分为四种情况,在首项的左边和右边,在尾项的左边和右边。 通过判断 a[i],a[+1],a[j},a[j-1] 的关系,书写状态转移方程。

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 1005, mod = 19650827;

int n, a[MAXN];

int f[MAXN][MAXN], g[MAXN][MAXN];
int sovle()
{
    for(int i=0;i<n;i++)  //初始值,只有一个人的区间只有1种情况
        f[i][i]=1;
    for(int len=1;len<n;len++) 
        for(int i=0;i+len<n;i++)
        {
            f[i][i+len]=( f[i+1][i+len]*(a[i]<a[i+1]) + 
                          g[i+1][i+len]*(a[i]<a[i+len]) )%mod;  
            g[i][i+len]=( f[i][i+len-1]*(a[i+len]>a[i]) +
                          g[i][i+len-1]*(a[i+len]>a[i+len-1]) )%mod;
        }
    return (f[0][n-1]+g[0][n-1])%mod;
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=0;i<n;i++)  
        cin>>a[i];
    cout<<sovle()<<endl;
    return 0;
}
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值