题目来源链接
链接: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;
}
总结
这个题用动态规划来进行分类讨论,是一个很好的思路。