codeforces713C(用第几大的数表示一个状态)
解题思路
先让\(a_{i}-i\),这样的话,就可以将问题转换成求最少操作数得到单调不减数列
用\(dp[i][j]\)表示前i个数变成小于等于j最少需要多少步,所以接下来考虑\(dp[i+1][j]\)怎么来,和\(dp[i][j]\)相比,他多了一个\(a_{i+1}\),所以只需要让\(a_{i+1}\)等于\(j\)就行,也就是
\[dp[i+1][j]=dp[i][j]+|a_{i+1}-j|
\]
光是这样还不够,还有可能是让前\(i\)个数小于等于\(j\)更优,也就是\(dp[i][j-1]\)的结果更优,所以就这样合并一下就有
\[dp[i][j]=min(dp[i-1][j]+|a_{i}-j|,dp[i][j-1])
\]
最终答案就是\(dp[n][n]\),考虑到j可以非常的大,所以可以对a进行排序,j表示第j小的数,这样可以省去大量的空间
代码
#include<bits/stdc++.h>
#define int long long
#define inf 1e18
#define MOD 1000000007
using namespace std;
int dp[3005][3005]={0};
void solve(){
int n;
cin>>n;
vector<int>a(n+1),b(n+1);
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=b[i]=a[i]-i;
}
sort(b.begin()+1,b.begin()+n+1);
for(int i=1;i<=n;i++)
{
dp[i][0]=inf;
for(int j=1;j<=n;j++)
{
dp[i][j]=min(dp[i][j-1],dp[i-1][j]+abs(a[i]-b[j]));
}
}
cout<<dp[n][n]<<endl;
}
signed main() {
ios::sync_with_stdio(0);
cout.tie(0),cin.tie(0);
int test=1;
while(test--)
solve();
return 0;
}
int
反思总结
- 对于dp的状态可以考虑用第几大的数表示一个状态