Description
一只队伍在爬山时碰到了雪崩,他们在逃跑时遇到了一座桥,他们要尽快的过桥. 桥已经很旧了, 所以它不能承受太重的东西. 任何时候队伍在桥上的人都不能超过一定的限制. 所以这只队伍过桥时只能分批过,当一组全部过去时,下一组才能接着过. 队伍里每个人过桥都需要特定的时间,当一批队员过桥时时间应该算走得最慢的那一个,每个人也有特定的重量,我们想知道如何分批过桥能使总时间最少.
Input
第一行两个数: w – 桥能承受的最大重量(100 <= w <= 400) 和 n – 队员总数(1 <= n <= 16). 接下来n 行每行两个数分别表示: t – 该队员过桥所需时间(1 <= t <= 50) 和 w – 该队员的重量(10 <= w <= 100).
Output
输出一个数表示最少的过桥时间.
Solution
数据一眼状压。
看看w和n并不能直接一个一个上,而且题目一批一批过明示是子集dp。
无脑预处理子集。
每个状态sta枚举lim^sta的子集以更新。
虽然看起来会爆,但其实仔细想想均摊一下并不会爆。
时间复杂度:
∑
i
=
0
l
i
m
s
(
i
)
+
∑
i
=
0
l
i
m
C
n
i
×
2
n
−
i
\sum_{i=0}^{lim}s(i)+\sum_{i=0}^{lim}C_{n}^i\times2^{n-i}
∑i=0lims(i)+∑i=0limCni×2n−i
即预处理+dp,其中
l
i
m
=
2
n
−
1
lim=2^n-1
lim=2n−1,s(i)表示子集个数。
Code
#include<bits/stdc++.h>
using namespace std;
int w[20],t[20],m,n;
int a[1<<17][2],tot;
int dp[1<<17];
map<int,int>mp;
int lowbit(int x){
return x&-x;
}
int main(){
scanf("%d%d",&m,&n);
int lim=(1<<n)-1;
for(int i=1,s=1;i<=n;i++,s*=2){
scanf("%d%d",&t[i],&w[i]);
mp[s]=i;
}
for(int i=0;i<=lim;i++){
int sum=0;
for(int s=i,j=lowbit(i);i&&j;s-=j,j=lowbit(s)){
//cout<<s<<" "<<j<<" "<<mp[j]<<endl;
sum+=w[mp[j]];
if(sum>m) break;
a[i][1]=max(a[i][1],t[mp[j]]);
}
if(sum<=m) a[i][0]=1;
//cout<<i<<" "<<a[i][0]<<" "<<a[i][1]<<endl;
}
for(int i=1;i<=lim;i++) dp[i]=2e9;
for(int i=0;i<=lim;i++){
int s=lim^i;
for(int j=s;j;j=(j-1)&s)
if(a[j][0]) dp[i|j]=min(dp[i|j],dp[i]+a[j][1]);
//cout<<i<<" "<<dp[i]<<endl;
}
printf("%d",dp[lim]);
}