码迷,mamicode.com
首页 > 其他好文 > 详细

2020-2-4摸底测试B

时间:2020-02-06 12:45:09      阅读:55      评论:0      收藏:0      [点我收藏+]

标签:记忆化   return   就是   第一个   背景   思路   博客   还需要   oid   

题目大意:

背景为弹钢琴,需要弹的钢琴琴键顺序为序列a(数字序列),弹钢琴只用一只手,五根手指从左到右标号1-5。设序列b为按键顺序,bi代表第i个键使用标号为bi的手指。aibi一一对应,当且仅当以下三种情况时,我们称b序列是合适的:

  对于任意1<=i<=n-1:

    ai<ai+1时,bi<bi+1

    ai>ai+1时,bi>bi+1

    ai=ai+1时,bi≠bi+1

要求求出一个合法的b序列,或者确定求不出

n<=105,ai<=2*105

解题思路(菜鸡瞎想):

一开始看到的时候,我觉得这应该就是个贪心,因为观察到样例给的答案里第一个数字不是1就是5,所以可能就有在某种情况尽可能用小的,在另一种情况尽可能用大的这样。

但实际操作发现是可以举出反例的...

后来看了dalao的博客,发现,对于每个bi,只有5中情况,虽然5n的复杂度不可忍受,但5n可以呀!

于是,思路就定为:记忆化搜索或者DP

表示状态:dp[i][j]表示第i个琴键用j手指能否弹完剩下的曲子

那么,我们就可以从n-1倒着搜(因为最后一个随便哪个手指都是合法的):

每搜到一个,根据上面的情况,从比当前键大/小/不等于的键中选一个键k,k满足dp[i+1][k]=true

也就是说当前键之后要弹得的那个键必须是可用的。

我们还需要一个ans[i][j]表示第i个键弹j如果可以的话之后要弹哪个,即ans i j =k用于回溯答案

最后,我们从dp[1]中选一个可用的,沿着ans一路返回到dp[n],边回溯边输出答案

以下是代码:

#include <cstdio>
int N,a[100005];
int ans[100005][6];
bool dp[100005][6];
void print(int x){
    int i=1;
    while(x>0){
        printf("%d ",x);
        x=ans[i++][x];
    }
}
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++)scanf("%d",&a[i]);
    for(int i=1;i<=5;i++)dp[N][i]=true;
    for(int i=N-1;i;i--){
        for(int j=1;j<=5;j++){
            if(a[i]<a[i+1]){
                for(int k=j+1;k<=5;k++){
                    if(dp[i+1][k]){
                        dp[i][j]=true;
                        ans[i][j]=k;
                        break;
                    }
                }
            }
            else if(a[i]>a[i+1]){
                for(int k=1;k<j;k++){
                    if(dp[i+1][k]){
                        dp[i][j]=true;
                        ans[i][j]=k;
                        break;
                    }
                }
            }
            else if(a[i]==a[i+1]){
                for(int k=1;k<=5;k++){
                    if(k==j)continue;
                    if(dp[i+1][k]){
                        dp[i][j]=true;
                        ans[i][j]=k;
                        break;
                    }
                }
            }
        }
    }
    for(int i=1;i<=5;i++){
        if(dp[1][i]){
            print(i);
            return 0;
        }
    }
    printf("-1");
    return 0;
}

 

2020-2-4摸底测试B

标签:记忆化   return   就是   第一个   背景   思路   博客   还需要   oid   

原文地址:https://www.cnblogs.com/Zarax/p/12267908.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!