Loading

P10197 [USACO24FEB] Minimum Sum of Maximums P 题解

思维和代码结合的题。

题意

给定一个长度为 \(n\) 的序列 \(a\),有 \(k\) 个位置固定,其余位置可重排使得 \(\sum_{i=1}^{n-1}{\max(a_i,a_{i+1})}\) 最小。\(n\leq 300,k\leq 6\)

观察 1:

先利用经典 trick 把式子 \(\max(a_i,a_{i+1})\) 转化成 \(\frac{1}{2}(a_i+a_{i+1}+|a_i-a_{i+1}|)\),这个式子前两项都是固定,故问题转化成最小化 $$\sum_{i=1}^{n-1}{|a_i-a_{i+1}|}$$

观察 2:

我们考虑一下当 \(k=0\) 时的特殊情况。这时比较容易想到从小到大排代价最小。可以用调整法证明。或者更感性理解,一个较大的数如果左右都比它小,它的代价会算两次,但安排一个比它更大的就只会算一次,每个数都这样考虑就是按顺序排了。那么对于一般的情况,相当于 \(k\) 个固定的位置把序列分成了至多 \(k+1\) 段,每一段内部都与 \(k=0\) 时一样。设这一段两边固定的数是 \(L,R\),设 \(L\leq R\),那么把小的放左边更优。由于按顺序排列,那么这一段的代价只与最小值 \(x\) 和最大值 \(y\) 有关,即 $$cost=|L-x|+(y-x)+|R-y|$$ 所以代价就确定了,考虑如何为每个段分配。

观察 3:

如果有一组 \(x_i<x_j<y_i<y_j\),即两个段的区间交叉,那么我们可以不停的交换 \(i\) 的最大值和 \(j\) 的最小值,直到 \(y'_i\leq x'_j\),这样使得 \(x_j\) 变大,\(y_i\) 变小,分类讨论可以发现代价只会变小。这证明了这个结构只会嵌套或者相离,不会交叉。由此我们才可以使用区间 dp。

状态设计及转移

将未固定的数排序,定义为 \(b_1,\dots,b_m\)。考虑到 \(k\) 很小,可以状压。设状态 \(dp_{l,r,S}\) 表示区间 \([l,r]\) 中选出数字刚好填满 \(S\) 集合的最小代价。设 \(len-r-l+1\)\(S\) 集合所需的数字个数 \(sum_S\)。显然只有 \(len\geq sum_S\) 时可以转移。接下来考虑:

  1. 缩小范围,把左边或右边的数扔掉,留个别的段,方程是 $$dp_{l,r,S}=\min(dp_{l+1,r,S},dp_{l,r-1,S})$$ 这个任何情况都可以转移。这部分 \(O(1)\)
  2. 嵌套转移,枚举集合 \(S\) 中的某段 \(x\) 作为外面的大段,只有 \(len=sum_S\) 时可以。此时根据观察 3,区间最小值 \(b_l\) 和最大值 \(b_r\) 显然是属于 \(x\),那么方程为 $$dp_{l,r,S}=\min_{x\in S}{\Big(dp_{l+1,r-1,S-{x}}+cost(x,b_l,b_r)\Big)}$$ 在 \(x\) 中间的其它数会在内部状态的转移 1 解决。这部分是 \(O(k)\)
  3. 相离转移,也就是拼接两个集合,枚举 \(S\) 的非空真子集 \(T\),方程为 $$dp_{l,r,S}=\min_{T\subset S}{(dp_{l,l+sum_T-1,T},dp_{l+sum_T,r,S-T})}$$ 那些填在中间的数也会在转移 1 解决。这部分是 \(O(3^k)\)

那么最后总时间复杂度 \(O(3^kn^2)\)

代码

实现有点复杂,好在比较清晰。

#include<bits/stdc++.h>
#define L(a,b,c,d) for(int a=b;a<=c;a+=d)
#define R(a,b,c,d) for(int a=b;a>=c;a-=d)

using namespace std;
typedef long long i64;
typedef __int128 i128;

const int N=300+5,INF=1<<20;

void solve();
int n,m,k,ans;
vector<int> a,pos,sl,sr,req,nums,sreq,dp[N][N];
vector<bool> f;

signed main(){
  ios::sync_with_stdio(0);
  cin.tie(0);
  int Test=1;
//  cin>>Test;
  while(Test--) solve();
  return 0;
}

void solve(){
  cin>>n>>k;
  a.resize(n+2);
  ans=0;
  L(i,1,n,1){
    cin>>a[i];
    ans+=a[i]*2;
  }
  a[0]=INF,a[n+1]=INF;
  ans+=2*INF;
  pos.resize(n+2);
  f.assign(n+2,0);
  L(i,1,k,1){
    cin>>pos[i];
    f[pos[i]]=1;
  }
  pos[0]=0,pos[k+1]=n+1;
  L(i,1,n,1){
    if(!f[i]) nums.push_back(a[i]);
  }
  nums.push_back(-1);
  sort(nums.begin(),nums.end());
  m=nums.size()-1;
  int seg=0;
  req.resize(k+1),sl.resize(k+1),sr.resize(k+1);
  L(i,0,k,1){
    if(pos[i]==pos[i+1]-1){
      ans+=abs(a[pos[i]]-a[pos[i+1]]);
      continue;
    }
    req[seg]=pos[i+1]-pos[i]-1;
    sl[seg]=min(a[pos[i]],a[pos[i+1]]);
    sr[seg]=max(a[pos[i]],a[pos[i+1]]);
    seg++;
  }
  sreq.resize(1<<seg);
  L(S,0,(1<<seg)-1,1){
    L(i,0,seg-1,1){
      if(S&(1<<i)) sreq[S]+=req[i];
    }
  }
  L(l,0,m+1,1){
    L(r,0,m+1,1){
      dp[l][r].assign(1<<seg,1<<30);
      dp[l][r][0]=0;
    }
  }
  L(len,1,m,1){
    L(l,1,m-len+1,1){
      int r=l+len-1;
      L(S,0,(1<<seg)-1,1){
        dp[l][r][S]=min(dp[l+1][r][S],dp[l][r-1][S]);
        if(sreq[S]>len) continue;
        else if(sreq[S]==len){
          L(i,0,seg-1,1){
            if(S&(1<<i)){
              if(req[i]==1&&len==1){
                dp[l][r][S]=min(dp[l][r][S],abs(sl[i]-nums[l])+abs(sr[i]-nums[l]));
              } 
              else if(req[i]>1){
                int T=S-(1<<i);
                int cost=abs(sl[i]-nums[l])+abs(sr[i]-nums[r])+nums[r]-nums[l];
                dp[l][r][S]=min(dp[l][r][S],dp[l+1][r-1][T]+cost);
              }
            }
          }
        }
        for(int T=(S-1)&S;T;T=(T-1)&S){
          int llen=sreq[T];
          dp[l][r][S]=min(dp[l][r][S],dp[l][l+llen-1][T]+dp[l+llen][r][S-T]);
        }
      }
    }
  }
  ans+=dp[1][m][(1<<seg)-1];
  ans/=2;
  ans-=2*INF;
  cout<<ans<<endl;
}
posted @ 2026-08-21 23:27  jess1ca1o0g3  阅读(2)  评论(0)    收藏  举报