状压dp 天上掉Pizza

原题:zjnu 1192

题意

买了一种披萨后会得到其他披萨的优惠券,问怎么卖能使单位面积的披萨价格最小(买一个两个随意)

解析

如果dp题做成了顺序题,就很难绕出来了

正确做法应该是枚举状态,由之前的状态转移而来。但是如果在想谁先谁后买,就是走歪路了

dp[i]表示状态为i时最小花费,eg:5个披萨i==29(11101)表示买了1、3、4、5种披萨的状态,状态转移方程为dp[i]=min(dp[i],dp[i-(1<<(j-1))]+w) (w为此时买第j个披萨所需要的花费)

代码

#include
#include
#include
#include
#include
using namespace std;

double ans;
int n,k;
double p[20],s[20];
double c[20][20];

double dp[1<<16],siz[1<<16];

int main(){
    while(scanf("%d",&n),n){
        ans=2.14e9;
        memset(c,0,sizeof(c));
        memset(siz,0,sizeof(siz));
        memset(dp,127,sizeof(dp));
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%d",p+i,s+i,&k);
            while(k--){
                int a,b;
                scanf("%d%d",&a,&b);
                c[i][a]=(100-b)*1.0/100.0;
            }
        }
        dp[0]=0;
        for(int i=1;i<(1<for(int j=1;j<=n;j++){
                int f=1;
                if((i&(1<<(j-1)))==0)continue;
                double w=p[j];
                for(int k=1;k<=n;k++){
                    if((i&(1<<(k-1)))==0)continue;
                    if(c[k][j]<1e-8)continue;
                    w*=c[k][j];
                }
                dp[i]=min(dp[i-(1<<(j-1))]+w,dp[i]);
                siz[i]=siz[i-(1<<(j-1))]+s[j];
            }
            //printf("dp[%d] is %.1lf,size is %.1f\n",i,dp[i],siz[i]);
            ans=min(ans,dp[i]/siz[i]);
        }
        printf("%.4lf\n",ans);
    }
}

你可能感兴趣的:(DP动态规划)