zoukankan      html  css  js  c++  java
  • POJ3709 K-Anonymous Sequence 斜率优化DP

    POJ3709 

    题意很简单 给n个递增整数(n<=500000)和一种操作(选择任意个数 使他们减少整数值) 使得对于所有的整数 在数列中 有k个相等的数

    O(n^2)的DP方程很容易得出 如下

    dp[i]=min{dp[j]+S[i]-S[j]-aj*(i-j)}| 0<=j<=i-k}

    进一步变形 设fj(x)=-aj*x+dp[j]-S[j]+aj*j

    可得 dp[i]=S[i]+min{fj(i)|0<=j<=i-k}

    也就是说 计算dp[i]相当于从i-k+1条直线中寻找在点x=i处的最小值。

    而dp[i+1]与dp[i]的区别仅在于需要多考虑一条直线,并且所求的x坐标值增加1.

    想想题目给我们的是一个递增的数列,而直线的系数为-aj 也就是说增加一条新的直线 它的斜率必然是最小的。

    下面尝试用队列维护这个直线的集合


    对于三条直线 f1 f2 f3 满足斜率递减 则在某种情况下,f2一定是多余的,可以删除。

    这种情况就是 f3与f1的交点在f2与f1的交点之前, 三条直线所组成的最小值折线一定没有f2的部分 所以可以删掉f2

    于是维护队列的方法就得到了

    i每增加1 当队头的直线不是最小值时则删除(因为它之后的直线斜率一定比它小)

    增加一条直线时,先删除所有多余的直线.

    显然队列的操作次数总共为n 所以算法总的复杂度为O(n)

    给出代码 比较简单

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<vector>
    #include<queue>
    using namespace std;
    const int MAXN=500000+1;
    typedef long long int LL;
    int n,k;
    LL a[MAXN],S[MAXN],dp[MAXN];
    int deq[MAXN];
    
    LL f(int j,int x)
    {
     return -a[j]*x+dp[j]-S[j]+a[j]*j;
    }
    
    bool check(int f1,int f2,int f3)
    {
     LL a1=-a[f1],b1=dp[f1]-S[f1]+a[f1]*f1;
     LL a2=-a[f2],b2=dp[f2]-S[f2]+a[f2]*f2;
     LL a3=-a[f3],b3=dp[f3]-S[f3]+a[f3]*f3;
     return (a2-a1)*(b3-b2)>=(b2-b1)*(a3-a2);
    }
    
    
    void solve()
    {
     for(int i=0;i<n;i++) S[i+1]=S[i]+a[i];
     
     int s=0,t=1;
     dp[0]=0;
     for(int i=k;i<=n;i++)
     	{
     	 if(i-k>=k)
     	 	{
    		  
     	 	while(s+1<t&&check(deq[t-2],deq[t-1],i-k))t--;
     	 	deq[t++]=i-k;
     	 	}
     	 while(s+1<t&&f(deq[s],i)>=f(deq[s+1],i))s++;
     	 
     	 dp[i]=S[i]+f(deq[s],i);
    	}
     printf("%lld
    ",dp[n]);
    }
    
    int main()
    {freopen("t.txt","r",stdin);
     int T;
     scanf("%d",&T);
     while(T--)
     	{
     	 scanf("%d%d",&n,&k);
     	 for(int i=0;i<n;i++)
     	 	scanf("%lld",&a[i]);
     	 solve();
    	}
     return 0;
    }
    

      

  • 相关阅读:
    图片处理中的Dithering技术
    网络I/O模型
    并发编程(二)
    并发编程(一)
    socket编程(二)
    socket编程(一)
    异常处理
    软件开发规范
    面向对象进阶
    多态与封装
  • 原文地址:https://www.cnblogs.com/heisenberg-/p/6656127.html
Copyright © 2011-2022 走看看