牛客周赛 Round 48 小红的乘2除2

题目来源链接

链接:https://ac.nowcoder.com/acm/contest/85187/D
来源:牛客网


题目

题目描述

小红拿到了一个数组。她需要进行恰好一次以下操作:
先使得一个元素除以2,向下取整。然后使得一个元素乘以2。
小红希望操作后,数组的陡峭值尽可能小。你能帮帮她吗?
定义数组的陡峭值为:相邻两数之差的绝对值之和。例如,[2,3,1]的陡峭值为|2-3|+|3-1|=3。

输入描述

第一行输入一个正整数 𝑛 ,代表数组大小。
第二行输入n个正整数 a i a_i ai 代表小红拿到的数组。

输出描述

一个整数,代表最终数组的陡峭值的最小值。

输入示例

1.

3
2 3 2

2.

4
1 2 3 4

输出示例

1.

0

2.

2

样例说明

1.先对第二个元素除以2,然后对第二个元素乘以2即可。
2.先对第四个元素除以2,然后对第一个元素乘以2。


思路:

借鉴牛客大佬代码,使用动态规划记录当前数字如何操作的状态。

代码及解析

#include<bits/stdc++.h>
using namespace std;
#define endl '\n' 
int n;
const int N=1e5+10;
int a[N];
long long dp[N][9];
/*
dp[i][0] 表示第i个数不操作,且之前未操作过
dp[i][1] 表示第i个数不操作,且之前进行过x操作
dp[i][2] 表示第i个数不操作,且之前进行过/操作
dp[i][3] 表示第i个数不操作,且之前进行了两次操作

dp[i][4] 表示第i个数x操作,前i-1个数未操作
dp[i][5] 表示第i个数x操作,之前进行了/操作
dp[i][6] 表示第i个数/操作,之前没有操作
dp[i][7] 表示第i个数/操作,之前进行*操作
dp[i][8] 表示第i个数进行/*操作
*/
int main() {
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    //如果第1个数没操作,前面没有数,不可能进行操作,因此将这三个初始化极大 
    dp[1][1] = 1e18;
	dp[1][2] = 1e18;
	dp[1][3] = 1e18;
    for(int i=2;i<=n;i++)
    {
        int t=abs(a[i]-a[i-1]);
        //1-i未操作过,此区间陡峭值不变
        dp[i][0]=dp[i-1][0]+t; 
        //1-i-1进行过*,两种可能:
        //一.i-1没有操作,但i-1之前进行*   对应dp[i-1][1] 
        //二.i-1进行*,i-1之前没有操作 对应dp[i-1][4]
        dp[i][1]=min(dp[i-1][1]+t,dp[i-1][4]+abs(a[i-1]*2-a[i]));
        //1 - i-1进行过/,两种可能:
        //一.i-1没有操作,但i-1之前进行/  对应dp[i-1][2]  
        //二.i-1进行/,i-1之前没有操作    对应dp[i-1][6]
        dp[i][2]=min(dp[i-1][2]+t,dp[i-1][6]+abs(a[i-1]/2-a[i]));
        //前i-1个数进行了两次操作,4种情况:
        // 一.i-1未操作,i-1之前的数进行了两次操作 dp[i-1][3]
        // 二.i-1进行*操作,i-1之前进行/操作      dp[i-1][5]
        // 三.i-1进行/操作,i-1之前进行*操作      dp[i-1][7]
        // 四.i-1进行两次操作                    dp[i-1][8]
        dp[i][3]=min({dp[i-1][3]+t,dp[i-1][5]+abs(a[i]-a[i-1]*2),dp[i-1][7]+abs(a[i]-a[i-1]/2),dp[i-1][8]+abs(a[i]-a[i-1]/2*2)});
        //第i个数*2 
        dp[i][4]=dp[i-1][0]+abs(a[i]*2-a[i-1]);
        //1 - i-1  进行过/ 两种情况
		//i-1 未操作,i-1之前进行/ dp[i-1 ][2]
		//i-1 进行/,之前未操作    dp[i-1][6]
        dp[i][5]=min(dp[i-1][2]+abs(a[i]*2-a[i-1]),dp[i-1][6]+abs(a[i]*2-a[i-1]/2));
        //i/2,之前未操作 
        dp[i][6]=dp[i-1][0]+abs(a[i]/2-a[i-1]);
        //i/2,之前进行过*操作 两种情况 
		//i-1 未操作 之前进行过* dp[i-1][1] 
		//i-1 进行*,之前未操作  dp[i-1][4]
        dp[i][7]=min(dp[i-1][1]+abs(a[i]/2-a[i-1]),dp[i-1][4]+abs(a[i]/2-a[i-1]*2));
        //第i个数进行两次操作 
        dp[i][8]=dp[i-1][0]+abs(a[i]/2*2-a[i-1]);
    }
    //结果有四种情况 1.n未操作,之前进行两次  2.n进行* 之前进行/  3.n/ 之前*  4.n两次操作 
    cout<<min({dp[n][3],dp[n][5],dp[n][7],dp[n][8]})<<endl;
    return 0;
}


总结

这个题用动态规划来进行分类讨论,是一个很好的思路。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值