• 【HDU3480】Division-DP+四边形不等式优化+贪心


    测试地址:Division

    题目大意:在一个有N个元素的整数集合S中选出M个子集,使得这M个子集的并为S,并且使总花费最小。选择一个子集的花费是(该子集中最大的元素-该子集中最小的元素)^2。

    做法:首先贪心分析,我们选择的子集在排过序后的数列中肯定是连续的一段,因为如果选择的子集不连续,则一定不比选择连续的更优,而且我们知道这些子集一定不会相交,所以我们就可以将原数列排序后DP。设dp(i,j)为前j个元素分为i个子集的最小花费,w(i,j)为选择从i到j的元素为一个子集的花费,可以得知w(i,j)=(S[j]-S[i])^2,状态转移方程为:dp(i,j)=min(dp(i-1,k-1)+w(k,j)) (i≤k≤j),这是一个O(N^2*M)的方程,直接做肯定无法通过。于是我们考虑优化,我们可以看出这个式子是一个典型的区间型DP的式子,自然而然的联想到用四边形不等式优化。设s(i,j)为使dp(i,j)最小的最大的决策变量k,只要证明出w满足区间包含单调性而且满足四边形不等式,就能推出dp满足四边形不等式,然后就可以推出s单调,然后就能优化了。这里只写出w的证明(不要问我为什么,后面的证明我也不会......):

    设i≤i'<j≤j',则:

    区间包含单调性:即证明w(i,j')≥w(i',j),这个根据w的定义是显然的。

    四边形不等式:即证明w(i,j)+w(i',j')≤w(i,j')+w(i',j)。把式子展开后左边得S[i]^2-2S[i]S[j]+S[j]^2+S[i']^2-2S[i']S[j']+S[j']^2,右边得S[i]^2-2S[i]S[j']+S[j']^2+S[i']^2-2S[i']S[j]+S[j]^2。两边减去S[i]^2+S[j]^2+S[i']^2+S[j']^2,再除以-2,于是我们只需证明S[i]S[j]+S[i']S[j']≥S[i]S[j']+S[i']S[j]即可。用左边减去右边,整理得到(S[i']-S[i])(S[j']-S[j]),由于S是升序的,根据条件得到这个式子≥0,定理得证。

    因为s单调,所以s(i-1,j)≤s(i,j)≤s(i,j+1),于是缩小了DP时决策变量的枚举范围,根据一通证明可以知道改进后的方程的复杂度是O(NM),可以通过该题。而且由于本题的状态方程有特殊性,所以空间可以通过滚动数组压缩到O(N)。

    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define inf 1000000000
    using namespace std;
    int T,n,m,a[10010];
    int dp[2][10010],s[2][10010];
    
    bool cmp(int a,int b)
    {
      return a<b;
    }
    
    int main()
    {
      scanf("%d",&T);
      for(int t=1;t<=T;t++)
      {
        scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    	sort(a+1,a+n+1,cmp);
    	
    	memset(s,0,sizeof(s));
    	int past=0,now=1;
    	for(int i=1;i<=n;i++) dp[past][i]=inf;
    	for(int i=1;i<=n;i++) s[past][i]=1;
    	for(int i=1;i<=m;i++)
    	{
    	  s[now][n+1]=n;
    	  for(int j=n;j>=i;j--)
    	  {
    	    dp[now][j]=inf;
    	    for(int k=s[past][j];k<=s[now][j+1];k++)
    		  if (dp[now][j]>=dp[past][k-1]+(a[j]-a[k])*(a[j]-a[k]))
    		  {
    		    dp[now][j]=dp[past][k-1]+(a[j]-a[k])*(a[j]-a[k]);
    			s[now][j]=k;
    		  }
    	  }
    	  swap(past,now);
    	}
    	
    	printf("Case %d: %d
    ",t,dp[past][n]);
      }
      
      return 0;
    }
    


  • 相关阅读:
    【数学】三分法
    【数学】【背包】【NOIP2018】P5020 货币系统
    【数学】【CF27E】 Number With The Given Amount Of Divisors
    【单调队列】【P3957】 跳房子
    【极值问题】【CF33C】 Wonderful Randomized Sum
    【DP】【CF31E】 TV Game
    【神仙题】【CF28D】 Don't fear, DravDe is kind
    【线段树】【CF19D】 Points
    【字符串】KMP字符串匹配
    【二维树状数组】【CF10D】 LCIS
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793734.html
Copyright © 2020-2023  润新知