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\) 时可以转移。接下来考虑:
- 缩小范围,把左边或右边的数扔掉,留个别的段,方程是 $$dp_{l,r,S}=\min(dp_{l+1,r,S},dp_{l,r-1,S})$$ 这个任何情况都可以转移。这部分 \(O(1)\)。
- 嵌套转移,枚举集合 \(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)\)。
- 相离转移,也就是拼接两个集合,枚举 \(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;
}
本文来自博客园,作者:jess1ca1o0g3,转载请注明原文链接:https://www.cnblogs.com/jess1ca1o0g3/p/22624050

浙公网安备 33010602011771号