Description
Mr_H出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:
试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个整数,分别表示这n个待排序的数。
例如:3 4 2 ?1 4 1 2 3 4,就表示有两组数据。第一组有3个数(4,2,-1),第二组有4个数(1,2,3,4)。可是现在Mr_H做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数据有2个数(1,9),第二组数据有3个数,可是“3”后面并没有出现三个数,只出现了一个数“2”而已!
现在Mr_H需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1或-1,写个程序,计算最少需要多少步才能将数据改得合法。
Input
第一行一个整数m,表示Mr_H做的输入数据包含的整数个数。第二行包含m个整数a[i],每个整数的绝对值不超过10000。
Output
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
Sample Input 1
4
1 9 3 2
Sample Output 1
2
Sample Input 2
10
4 4 3 5 0 -4 -2 -1 3 5
Sample Output 2
3
Hint
对于20%的数据,m<=10, |a[i]|<=5;
对于60%的数据,m<=5000, |a[i]|<=10000;
对于100%的数据,m<=100000, |a[i]|<=10000.
DP题,先设状态函数dp(i),表示将以第i个数结尾的数据修改为合法需要的最少步数
边界dp(0)=0
Ans=dp(m)
转移方程(dp(i)=min{dp(j)+|a[j+1]-(i-(j+1))|} | 0<=j<i)
表示以a[j+1~i]为一组数据,则要将a[j+1]的值改为i-(j+1),求所有的j中的最小值
直接枚举j时间复杂度O(m*m)超时。
优化:
打开绝对值内的括号(dp(i)=min{dp(j)+|a[j+1]+j+1-i|} | 0<=j<i)
再将绝对值展开得到
现在我们用val(j)表示dp(j)+a[j+1]+j+1,用x(j)表示a[j+1]+j+1
然后用一个堆,维护val(j)的最小值,将堆顶所有x(j)<i的弹出
计算最小的tmp = val(q.top()) - 2x(q.top()) = dp(j)+a[j+1]+j+1 - 2(a[j+1]+j+1) = dp(j) - a[j+1] - j - 1,
即为2式要维护的最小值,得到的最小tmp再+i即为满足2式条件的最优结果
因为i从小到大,所以满足x(j)小于当前i的j一定满足小于后面的i,
因此tmp不需要每次重新计算,只要每次和堆顶比较即可
弹出结束后如果堆不空,则堆顶满足x(q.top())>=i,又因为是按val()排序,
此时的val(q.top())-i即为满足1式条件的最优结果,然后dp(i)取上面得到的1式2式最优结果的最小值即可
以下思路来源:https://blog.csdn.net/its_elaine/article/details/76034801
对于这个思路,可能有疑惑的是,如果k满足x(k)<i,但因为val(k)的值较大而被压在堆里,是否会漏情况导致未得到2式的最优解。
结果是,不会。
证明:若出现上述情况,则设停止弹出时堆根为j,有val(j)<=val(k)且x(k)<i<=x(j)
将val(j)<=val(k)展开为dp(j) + x(j) <= dp(k) + x(k)
移项得dp(j) - dp(k) + x(j) <= x(k) ①
此时dp(i)由j转移,结果是dp(j) + x(j) - i。
如果由k转移,结果是dp(k) - x(k) + i。
上减下得dp(j) - dp(k) + x(j) + x(k) - 2i
由①得,该式<= 2x(k) -2i
因为 x(k)<i,所以 2x(k) -2*i < 0,所以该式<0
即由j转移比由k转移更优,证明了没有考虑k不会影响到dp(i)值的计算。
但是k仍有保留在堆中的价值,因为当i增大到x(j)<i时由j转移的值会按2式计算,与由k转移的值大小不能直接依靠上式推导出来,需要靠tmp取最小值来取舍。
转移部分代码(堆依靠优先队列实现):
struct data{
int val;//dp[j]+a[j]+j+1
int x;//a[j]+j+1
bool operator<(const data &b)const{return val>b.val;}
};
priority_queue<data> q;
//main()
dp[0]=0;
int i=0;
while(i<m){
q.push((data){dp[i]+a[i+1]+i+1, a[i+1]+i+1});
i++;
while(!q.empty() && q.top().x<i){
tmp=min(tmp,q.top().val-q.top().x*2);
q.pop();
}
dp[i]=min(dp[i],tmp+i);
if(!q.empty())dp[i]=min(dp[i],q.top().val-i);
}