Crossing the Border 题解
题意:\(n\) 个物品,每个物品有价值 \(c_i\) 和重量 \(w_i\),给定 \(W\),现在要把所有物品划分成若干个集合,且每个集合的物品重量之和不超过 \(W\),定义一个集合的权值为其中物品价值的最大值,问所有集合的权值之和的最小值是多少,以及达成最小值的划分方案。
不妨先把所有物品按照 \(c\) 排序,设 \(sum(S)\) 表示集合 \(S\) 的重量之和,\(high(S)\) 表示集合 \(S\) 中编号最大的物品的编号。
不难想到 \(O(3^n)\) 的状压 DP,直接设 \(f_S\) 表示划分 \(S\) 中的点的答案。为了方便优化,我们不妨钦定在进行转移 \(f_{S}+c_{high(T/S)} \to f_{T}\) \((sum(T/S)\le W)\) 时,\(high(T)>high(S)\),也就是说 \(high(T/S)=high(T)\)。
然后我也不知道怎么想到的,但是我们考虑折半来优化这个转移,我们把每个 \(S\) 写成 \(LR\) 的形式,上面的转移改写成 \(f_{LR}+c_{high(R')} \to f_{L'R'}\),其中 \(L,R,L',R'\) 满足(如果 \(R'=0\) 就特殊处理,跑一开始的暴力 DP,下面默认 \(R'\ne 0\)):
- \(L\subseteq L',R\subseteq R'\)。
- \(high(R)\ne high(R')\)。
- \(sum(L'/L)+sum(R'/R)\le W\)。
转移系数 \(c_{high(R')}\) 跟 \(R'\) 有关,所以我们考虑枚举 \(R'\),如果你再枚举 \(L'\) 就跟一开始的 DP 本质相同了,折半了个寂寞,于是我们考虑再枚举 \(L\)。也就是说,我们要在枚举 \(L,R'\) 之后处理掉所有 \(f_{LR} \to f_{L'R'}\) 的转移。
取出所有满足条件 1 的 \(L'\),并取出所有满足条件 1,2 的 \(R\),把他们分别按照 \(sum(L'/L),sum(R'/R)\) 排序,每一个 \(R\) 的答案 \(f_{LR}+c_{high(R')}\) 是确定的,而对于一个 \(L'\) 合法的 \(R\) 是排序后的一段前缀,因此可以枚举 \(L'\),双指针求出所有满足条件 3 的 \(R\) 的答案之和,贡献到 \(f_{L'R'}\) 即可。
注:这里的排序是可以预处理的,不用枚举完 \(L,R'\) 之后再排序。
复杂度 \(O(2^{\frac{n}{2}}3^{\frac{n}{2}}+3^{\frac{n}{2}}n)=O(6^{\frac{n}{2}}+3^{\frac{n}{2}}n)\)。其中 \(O(3^{\frac{n}{2}}n)\) 是预处理排序的复杂度。
code
#include<bits/stdc++.h>
#define Debug puts("-------------------------")
#define eb emplace_back
#define fi first
#define se second
using namespace std;
const int N=22+5,mod=998244353;
pair<int,int> operator + (const pair<int,int> &x,const pair<int,int> &y){
if(x.se==0) return y;
if(y.se==0) return x;
if(x.fi==y.fi) return {x.fi,(x.se+y.se)%mod};
return min(x,y);
}
pair<int,int> operator + (const pair<int,int> &x,int d){ return {x.fi+d,x.se}; }
int n,n1,n2,W,w[N],c[N],sum[(1<<22)+5],high[(1<<22)+5];
pair<int,int> f[(1<<11)+5][(1<<11)+5];
vector<pair<int,int> > SL[(1<<11)+5],SR[(1<<11)+5];
void Init(){
for(int S=0;S<(1<<n);S++){
high[S]=-1;
for(int i=0;i<n;i++) if(S>>i&1) high[S]=i+1,sum[S]+=w[i+1];
}
n1=n/2,n2=n-n1;
for(int S=0;S<(1<<n1);S++){
for(int L=S;;L=(L-1)&S){
SL[L].eb(make_pair(sum[S^L],S));
if(!L) break;
}
}
for(int L=0;L<(1<<n1);L++){
sort(SL[L].begin(),SL[L].end());
}
for(int R=1;R<(1<<n2);R++){
for(int S=R;;S=(S-1)&R){
if(high[R]==high[S]) continue;
SR[R].eb(make_pair(sum[(R^S)<<n1],S));
if(!S) break;
}
sort(SR[R].begin(),SR[R].end());
}
f[0][0]={0,1};
for(int S=1;S<(1<<n1);S++){
for(int T=S;T;T=(T-1)&S){
if(high[T]==high[S]&&sum[T]<=W) f[S][0]=f[S][0]+(f[S^T][0]+c[high[T]]);
}
}
}
signed main(){
double beg=clock();
scanf("%d%d",&n,&W);
vector<pair<int,int> > vec(n);
for(int i=1;i<=n;i++) scanf("%d%d",&vec[i-1].se,&vec[i-1].fi);
sort(vec.begin(),vec.end());
for(int i=1;i<=n;i++) w[i]=vec[i-1].se,c[i]=vec[i-1].fi;
Init();
int all1=(1<<n1)-1,all2=(1<<n2)-1;
for(int l=0;l<(1<<n1);l++){
for(int R=1;R<(1<<n2);R++){
int siz1=SL[l].size(),siz2=SR[R].size();
pair<int,int> res={0,0};
for(int i=siz1-1,j=0;i>=0;i--){
int L=SL[l][i].se;
while(j<siz2&&SL[l][i].fi+SR[R][j].fi<=W){
int r=SR[R][j].se;
res=res+(f[l][r]+c[high[R<<n1]]);
++j;
}
f[L][R]=f[L][R]+res;
}
}
}
printf("%d %d\n",f[all1][all2].fi,f[all1][all2].se);
cerr << "Time: " << (clock()-beg) << endl;
return 0;
}

浙公网安备 33010602011771号