POJ3666
题意
给n个的序列a[i],你可以对每个数+1或者-1,使之成为单调不递减/不递增序列的最小值,问最小的修改数之和
分析
可以推倒出一个结论,每个数最后必然是原序列中的数,可以简单推一下
然后我们根据题意不递减就可以想出将原序列排序,去重下,然后枚举每个数变成那个数即可
dp
定义:dp[i][j]:前i个数,其中第i个数变成第j大的数字的最先修改的和
转移:显然dp[i][j]需要dp[1][j-1]~dp[i-1]的最小值,由于j是从小到大枚举的,直接记录下前缀最小即可,答案即为dp[n][1]~dp[n][m]中最小值
然后把j逆序枚举下就可以求出不递增的情况最小值
#include<cstdio> #include<iostream> #include<algorithm> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) #define pii pair<int,int> #define fi first #define se second #define mp make_pair using namespace std; const int maxn = 2000+10; const int maxm = 1e5+10; int a[maxn],b[maxn]; int n; int dp[maxn][maxn]; int main() { scanf("%d", &n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } ll sum=1ll<<60; sort(b+1,b+n+1); int ans=unique(b+1,b+n+1)-b; for(int i=1;i<=ans-1;i++) { // cout<<b[i]<<' '; dp[1][i]=abs(b[i]-a[1]); } for(int i=2;i<=n;i++) { ll temp=1ll<<60; for(int j=1;j<=ans-1;j++) { temp=min(temp,1ll*dp[i-1][j]); //if(i==n) // cout<<temp<<' '; dp[i][j]=temp; dp[i][j]+=abs(b[j]-a[i]); } //cout<<dp if(i==n) { for(int j=1;j<=ans-1;j++) { //cout<<dp[n][j]<<endl; sum=min(sum,1ll*dp[n][j]); } } } printf("%lld\n", sum); return 0; }
要么优秀要么生锈