zoukankan      html  css  js  c++  java
  • 【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;
    }
    


  • 相关阅读:
    【项目】项目27
    【项目】项目26
    【项目】项目25
    【项目】项目24
    【项目】项目23
    【项目】项目22
    【项目】项目21
    【项目】项目20
    【项目】项目19
    【项目】项目18【项目】项目18
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793733.html
Copyright © 2011-2022 走看看