您的位置:首页 > 其它

Codeforces Round 371 C Sonya and Problem Wihtout a Legend

2016-09-20 13:26 411 查看
Task:

给你一个长度为n的序列,每次操作能使序列中的数+1或-1,求最少的操作数,使得序列严格递增.

Sample Input

5

5 4 3 2 1

Sample Output

12

Hint

1<=n<=3000

Solution:

首先我们先明白一件事:我们有A[i]<A[i+1],也就可以转化为A[i]−i<=A[i]−i−1,那么我们在一开始将序列中的数减去i,题目就变成了将n个数通过最少的操作数变成一个递增的序列,这个可以通过O(n2)的dp做到.

定义dp[i][j]为前i个点,最后一个数为j的最小操作数.

就可以很简单地写出dp方程了:

dp[i][j]=min(dp[i−1][k]+|j−k|)

那么:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define M 3005
#define LL long long
using namespace std;
int pl[M],a[M];
LL dp[M][M];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
a[i]-=i;
pl[i-1]=a[i];
}
sort(pl,pl+n);
int sz=unique(pl,pl+n)-pl;
for(int i=1;i<=n;i++)a[i]=lower_bound(pl,pl+sz,a[i])-pl;
for(int i=0;i<sz;i++)dp[1][i]=abs(pl[a[1]]-pl[i]);
for(int i=2;i<=n;i++){
LL Mi=dp[i-1][0];
for(int j=0;j<sz;j++){
if(Mi>dp[i-1][j])Mi=dp[i-1][j];
dp[i][j]=Mi+abs(pl[a[i]]-pl[j]);
}
}
LL ans=-1;
for(int i=0;i<sz;i++)
if(ans==-1||ans>dp
[i])ans=dp
[i];
cout<<ans<<endl;
return 0;
}


这道题还有一种神奇的写法:

如果有一个数a,b(a< b),那么我们可以用(b-a)的代价来把他们同时变成[a,b]的任意一个数.然后如果再来一个大于a的数c,我们只需把a,b都变成比c小就好了,而如果c比a小,把c变成a就好了(这时费用加上这个差),后来如果又来了一个数,还是比a小(但是比c大),我们只需把刚才那个费用用来将a,b变小就行了.

总的来说,如果现在堆中的最大值比我现在的值大,我就把答案加上它们的差,表示将小的变成大的(也有可能在后来它们会一起变小),否则我们就把它放到堆里,留给以后的数字将它变小.

那么用一个堆来维护数字,然后遇见堆中的最大值比现在的值大的时候,我们就把答案加上堆中最大值减去当前的数,再把堆中最大的数删去,加进当前的这个数.

最后输出答案就好了.

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
priority_queue<int>q;
int main(){
long long ans=0;
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
x-=i;
q.push(x);
if(q.top()>x){
ans+=q.top()-x;
q.pop();
q.push(x);
}
}
cout<<ans<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: