xdoj 1234: 尧老师要教孩子玩球球

一场训练赛里的裸状压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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值