HihoCoder1532 : 最美和弦(DP优化)

描述

某个夜晚,Bob将他弹奏的钢琴曲录下来发给Jack,Jack感动之余决定用吉他为他伴奏。

我们可以用一个整数表示一个音符的音高,并可认为Bob弹奏的曲子是由3N个整数构成的一个序列。其中每个整数的取值范围是[-200, 200]。

Jack共弹奏 N 个和弦,每个和弦由三个音符组成。Jack可以自行决定和弦的第一个音符,其后的两个音符由第一个音符与和弦种类所决定。Jack共弹奏两种和弦:大三和弦与小三和弦。假设Jack决定某个和弦的第一个音符是 x,那么对于大三和弦,余下两个音符依序是 x+4和 x+7;对于小三和弦,余下两个音符依序是x+3和x+7。两个和弦相同,当且仅当其对应位置的三个音符都相同。其中每个和弦的第一个音符x的取值范围也是[-200, 200]。

Jack很懒,一旦决定弹奏某个和弦后,便不愿意更换和弦。即如果他开始弹奏1,5,8这个和弦,他将不停重复1,5,8,1,5,8,1,5,8……Bob觉得这样过于单调,于是Jack妥协:他表示愿意更换和弦,但最多更换K次。最开始选择和弦不计在更换次数内。

我们用不和谐值衡量乐曲与伴奏之间的契合程度。记某时刻Bob弹奏音符的音高为a,Jack弹奏音符的音高为b,则该点的不和谐值为|a-b|。整首乐曲的不和谐值等于这3N个不和谐值之和。

Jack希望选取最美的一组和弦,使得整首乐曲的不和谐值达到最小。你需要输出这个最小值。

输入

第一行两个正整数 N (≤1000), K (≤20).

第二行3N个整数(取值范围[-200, 200])为Bob的曲谱。

输出

一个整数,为乐曲最小不和谐值。

样例输入

3 1
-1 3 6 4 7 11 21 26 28

样例输出

15

思路:dp[N][X][K][1]表示第N个和弦,用了K次机会,最后一次用的是3还是4。

每一次,都可以选择换或者不换,换的时候前面一次的X与现在的X不同,因此需要for循环枚举X,但是这样复杂度太高。需要记录前面用那个X最小,及代码里的Min。

那么最近经常做到记录前面最优的DP,这里有两道区间题,需要前缀和优化DP:http://www.cnblogs.com/hua-dong/p/8452988.html

#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=1000000000;
int dp[1010][410][21][2],a[3010];
int Min[1010][21][2];
int abs(int x){ if(x<0) return -x; return x; }
int main()
{
    int N,K,i,j,k,ans=inf;
    scanf("%d%d",&N,&K);
    for(i=0;i<=N;i++)
     for(j=0;j<=K;j++)
       Min[i][j][0]=Min[i][j][1]=inf;
    for(i=1;i<=3*N;i++) scanf("%d",&a[i]);
    for(i=1;i<=N;i++){
        for(j=-200;j<=200;j++){ //K=0,显然不能换
            dp[i][j+200][0][0]=dp[i-1][j+200][0][0]+abs(a[3*(i-1)+1]-j)+abs(a[3*(i-1)+2]-j-3)+abs(a[3*(i-1)+3]-j-7);
            Min[i][0][0]=min(Min[i][0][0],dp[i][j+200][0][0]);
            dp[i][j+200][0][1]=dp[i-1][j+200][0][1]+abs(a[3*i-2]-j)+abs(a[3*i-1]-j-4)+abs(a[3*i]-j-7);
            Min[i][0][1]=min(Min[i][0][1],dp[i][j+200][0][1]);
        }
        for(k=1;k<=K;k++)
          for(j=-200;j<=200;j++){//K>0,当前j可能是换后的,可能没有换。
             dp[i][j+200][k][0]=min(dp[i-1][j+200][k][0],Min[i-1][k-1][0])+abs(a[3*i-2]-j)+abs(a[3*i-1]-j-3)+abs(a[3*i]-j-7);
             dp[i][j+200][k][0]=min(dp[i][j+200][k][0],Min[i-1][k-1][1]+abs(a[3*i-2]-j)+abs(a[3*i-1]-j-3)+abs(a[3*i]-j-7));
             Min[i][k][0]=min(Min[i][k][0],dp[i][j+200][k][0]);
             dp[i][j+200][k][1]=min(dp[i-1][j+200][k][1],Min[i-1][k-1][0])+abs(a[3*i-2]-j)+abs(a[3*i-1]-j-4)+abs(a[3*i]-j-7);
             dp[i][j+200][k][1]=min(dp[i][j+200][k][1],Min[i-1][k-1][1]+abs(a[3*i-2]-j)+abs(a[3*i-1]-j-4)+abs(a[3*i]-j-7));
             Min[i][k][1]=min(Min[i][k][1],dp[i][j+200][k][1]);
        }
    }
    for(k=0;k<=K;k++){
         ans=min(ans,Min[N][k][1]);
         ans=min(ans,Min[N][k][0]);
    }
    printf("%d\n",ans);
    return 0;
}

原文地址:https://www.cnblogs.com/hua-dong/p/8457125.html

时间: 2024-10-15 10:52:51

HihoCoder1532 : 最美和弦(DP优化)的相关文章

常见的DP优化类型

常见的DP优化类型 1单调队列直接优化 如果a[i]单调增的话,显然可以用减单调队列直接存f[j]进行优化. 2斜率不等式 即实现转移方程中的i,j分离.b单调减,a单调增(可选). 令: 在队首,如果g[j,k]>=-a[i],那么j优于k,而且以后j也优于k,因此k可以重队列中直接删去.在队尾,如果x<y<z,且g[x,y]<=g[y,z],也就是说只要y优于x一定可以得出z优于y的,我们就删去y. 经过队尾的筛选,我们在队列中得到的是一个斜率递减的下凸包,每次寻找从上往下被-

hdu5009 离散化+dp+优化

西安网络赛C题.先对大数据离散化,dp优化 #include<iostream> //G++ #include<vector> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<queue> #include<cmath> using namespace std; const int maxn=512

LCIS tyvj1071 DP优化

思路: f[i][j]表示n1串第i个与n2串第j个且以j结尾的LCIS长度. 很好想的一个DP. 然后难点是优化.这道题也算是用到了DP优化的一个经典类型吧. 可以这样说,这类DP优化的起因是发现重复计算了很多状态,比如本题k的那层循环. 然后就可以用maxl标记搞一下,将O(n^3)变成O(n^2). #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> usi

loj6171/bzoj4899 记忆的轮廊(期望dp+优化)

题目: https://loj.ac/problem/6171 分析: 设dp[i][j]表示从第i个点出发(正确节点),还可以有j个存档点(在i点使用一个存档机会),走到终点n的期望步数 那么 a[i][k]表示i点为存档点,从i点走到k点(正确节点)的期望步数(中间没有其它存档点) 那么a[i][j]可以递推预处理出 其中g[v]表示从一个错误节点v开始走,期望走g[v]步会读档 解方程可以解出 s[j-1]就是点j-1出去的所有错误儿子的g[v]之和 那么接下来只要知道如何求g[v]就行了

poj1088 滑雪(dfs、dp优化)

#include <iostream> #include <map> #include <string> #include <cstdio> #include <sstream> #include <cstring> #include <vector> #include <cmath> #define N 110 int a,b,step=0; int anw=0; int moun[N][N]; int dp

dp优化1——sgq(单调队列)

该文是对dp的提高(并非是dp入门,dp入门者请先参考其他文章) 有时候dp的复杂度也有点大...会被卡. 这几次blog大多数会讲dp优化. 回归noip2017PJT4.(题目可以自己去百度).就是个很好的案例.那题是个二分套dp如果dp不优化复杂度O(n^2logn)还能拿60分(CCF太仁慈了,如果是我直接给10分). 正解加上个单调队列(其实是sliding window)O(nlogn) 我们发现,此类dp是这样的 状态i是由[l,r]转移过来的.且i在向右移动的过程中,[l,r]一

7.14 单调栈 单调队列 +dp优化

单调栈和单调队列的定义具体看ppt了 模板: 单调队列 head =1; tail = 0; rep( i ,1 ,n ){ while( head <= tail && a[i] < dq[tail].first)tail--; while( head <= tail && dq[head].second < i-k+1) head++; dq[ ++tail ]={a[i] ,i}; 例题:https://vjudge.net/contest/3

dp优化总结

dp优化总结 一.滚动数组 典型的空间优化. 这应该是最最普通的一个优化了吧.. 对于某些状态转移第i个只需要用到第i-1个状态时,就可以用滚动数组,把第一维用0/1表示. 拓展1: 当一个状态转移要用到前m个转移时,我们依然可以滚起来,把第一维按模m的值滚起来. 拓展2: 若每一个决策可以选任意次(在一定限度下),那么我们可以借鉴完全背包的思路,把决策一个一个累计起来,起到优化时间的作用.(例题:NOIP2013 飞扬的小鸟) 二.数据结构优化 1.单调队列 如果一个转移方程模型大致如下: \

hihocoder [Offer收割]编程练习赛18 C 最美和弦(dp)

题目链接:http://hihocoder.com/problemset/problem/1532 题解:一道基础的dp,设dp[i][j][k][l]表示处理到第几个数,当前是哪个和弦错了几次初始x值是多少.这里还要再辅助一个val[k]表示处理到当前情况只错了k次的最小值是多少因为改变的不止是和弦还有初始值,可以看一下代码理解一下. #include <iostream> #include <cstring> #include <cstdio> #include &