一场训练赛里的裸状压dp,赛时没时间看,被自己代码变量输入时输反调了很久很久的bug,话说以后变量名得好好设置了。
赛后补题看了一下数据范围,n<=18(标准状压数据?),然后球不能并排放,然后每个球都是可以放入烟筒的,看完题目就知道其实水题一枚,状压算完全部状态就可以了。
但是这里有一个优化,就是对于如果放了4个球,枚举的集合S不能从0到1<<n,这样是会超时的,这里有个小优化。
对于集合S,枚举全部大小为i的子集
comb=(1<<i)-1;
while(comb<1<<n){
x=comb&-comb,y=comb+x;
comb=((comb&~y)/x>>1)|y;
}
然后dp[i][j]表示的是,对于球i放在顶端,j表示放了j状态的球在烟筒里的最低高度。
最后枚举dp[1~n][(1<<n)-1]的最小值(不为0)即可
ac代码.
#include <iostream>
#include <stdio.h>
#include <vector>
#include <string.h>
#include <algorithm>
#include <map>
#include <math.h>
#include<time.h>
#include<set>
using namespace std;
const int maxn=1e4+7;
double qq[20];
double dp[19][(1<<18)+7];
double W;
vector<int>q1;
vector<int>q2;
int main(){
int i,n,j,l,k,f1,f2,f3,f4,t1,t2,t3,t4,m;
//freopen("in.txt","r",stdin);
cin >> n;
for(i=0;i<n;i++)
cin >> qq[i];
cin >>W;
if(n==0){
cout <<"0.00"<<endl;
return 0;
}
W=W+W;
int S=(1<<n)-1;
for(i=0;i<n;i++){
dp[i][1<<i]=qq[i]+qq[i];
}
int len1,len2,len3;
double h;
i=1;
int comb;
int x,y;
while(i<n){
comb=(1<<i)-1;
while(comb<1<<n){
for(j=0;j<n;j++){
if(dp[j][comb]!=0){
for(k=0;k<n;k++){
if(comb>>k&1)continue;
h=qq[k]+dp[j][comb]+sqrt((qq[k]+qq[j])*(qq[k]+qq[j])-(W-qq[k]-qq[j])*(W-qq[k]-qq[j]))-qq[j];
if(dp[k][comb|(1<<k)]==0){
dp[k][comb|(1<<k)]=h;
}else
dp[k][comb|(1<<k)]=min(dp[k][comb|(1<<k)],h);
}
}
}
x=comb&-comb,y=comb+x;
comb=((comb&~y)/x>>1)|y;
}
i++;
}
double max1=1e9+7;
for(i=0;i<n;i++)
if(dp[i][S]!=0){
max1=min(max1,dp[i][S]);
// cout <<dp[i][S-1]<< endl;
}
printf("%.2f\n",max1);
return 0;
}