zoukankan      html  css  js  c++  java
  • cqyz oj | 【重庆市NOIP模拟赛】数据 | DP动态规划 | 堆优化

    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)
    再将绝对值展开得到

    [dp(i)=min left{ egin{array}{} min{dp(j)+a[j+1]+j+1}-i | a[j+1]+j+1>=i ……①\ min{dp(j)-a[j+1]-j-1}+i | a[j+1]+j+1<i ……②\ end{array} ight. ]

    现在我们用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
    由①得,该式<= 2
    x(k) -2i
    因为 x(k)<i,所以 2
    x(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);
    	}
    
  • 相关阅读:
    杭电2059
    杭电2058
    php错误大集合
    显示IP地址
    超简单好用的屏幕录像工具
    jquery“不再提醒"功能
    KindEditor编辑器中的class自动过滤了
    实用案例:切换面板同时切换内容
    仿51返利用户图解教程
    JavaScript调用dataTable并获取其值(ASP.Net,VS2005)
  • 原文地址:https://www.cnblogs.com/de-compass/p/11242008.html
Copyright © 2011-2022 走看看