此题倒是能用贪心骗点分...
其实对于每一个位置 , 我们知道最后的改善结果一定是原数列中的数 .
(因为要尽量减少消耗, 可以考虑减小至和相邻的相同) 有了这个结论之后, 我们就考虑用dp来做这件事情
首先 存下所有数据于 data[]
排序data 得到 data_sort[]
然后用dp[i][j]来表示 前i个元素 以data_sort[j]为结尾 转换成 递增序列的耗费.
那么我们可以知道
dp[i][j] = min({dp[i-1][k]}) + | data[i]- data_sort[j] |
所以直译为:
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) for (int k = 1; k <= j ; ++k) tmp = min(tmp,dp[i-1][k]); dp[i][j] = tmp + abs(arr[j]-data[i]);
但是可以有大大的优化, 因为第三重循环式为了计算从dp[i][1]到dp[i][j]的最小值, 我们可以利用一个数组来dp计算这个最小值.
所以用ass[i][j]来表示从dp[i][1]到dp[i][j]的最小值,
那么对于ass[i][j] 的更新 可有
ass[i][j] = min(dp[i][j],(j==1 ? INF : (ass[i][j-1])));
当j=1时,ass[i][1] = dp[i][1]
当j>=2时,ass[i][j] = min(dp[i][j],ass[i][j-1]);
所以要先更新dp[i][j]再更新ass[i][j]
因为j的顺序是从1到n, data_sort[j]是从小到大的,所以就维护了整体的单调性 从而得到了答案.
#include <iostream> #include <algorithm> #include <cstring> #include <cmath> using namespace std; typedef unsigned long long ull; const int MaxN = 2000+10; const int INF = 2147483600; int n; int data[MaxN]; int data_sort[MaxN]; int data_sort_d[MaxN]; int dp[MaxN][MaxN]; //dp[i][j] 表示前i个数 修补为以data_sort[j]为结尾的序列时的消耗最小体力值 int ass[MaxN][MaxN];//用来辅助求最小值的dp过程 bool cmp_int_d(const int& a, const int& b){ return a>b; } void init(){ cin>>n; for (int i = 1; i <= n; ++i) { cin>>data[i]; data_sort[i] = data[i]; } sort(data_sort+1,data_sort+n+1); for (int i = 1; i <= n; ++i) { data_sort_d[i] = data_sort[n+1-i]; } memset(dp,0,sizeof(dp)); memset(ass,0,sizeof(ass)); } int build(int* arr){ //cout<<"-----\n"; for (int i = 1; i <= n; ++i){ for (int j = 1; j <= n; ++j){ // for (int k = 1; k <= j ; ++k) // tmp = min(tmp,dp[i-1][k]); dp[i][j] = ass[i-1][j] + abs(arr[j]-data[i]); //cout<<dp[i][j]<<" "; ass[i][j] = min(dp[i][j],(j==1 ? INF : (ass[i][j-1]))); //cout<<"("<<ass[i][j]<<") "; } //cout<<endl; } int ans = INF; for (int i = 1; i <= n; ++i) ans = min(ans,dp[n][i]); return ans; } int main(int argc, char const *argv[]) { init(); int a = build(data_sort); int d = build(data_sort_d); //cout<<a<<" "<<d<<endl; cout<<min(a,d)<<endl; return 0; }
时间: 2024-11-03 03:45:17