P3052 [USACO12MAR]Cows in a Skyscraper G-二进制状压DP

状压DP特点是数据范围极小,在20以内。一般这样的题目可以通过暴力搜索与剪枝通过,但更优的方法莫过于状压DP。n种物体选与不选共有(1<<n)种方案,方案下界为0,上界为(1<<n)-1,也就是都选。

状压DP一般是前推后且无后效性。对于每一个位置,都要遍历0-(1<<n)全部状态,对于每一个状态,再枚举各个物体选与否的情况,已经选了就略过,否则前推后进行优化。

注意物体的元素下标最好从0开始,便于二进制表示。

dp[i][j]代表了开i个背包时,装下j状态时的情况,对于每一个j状态,考虑里面没有装的,能装在本背包时,就修改 dp[i][j|(1<<k)]=min(dp[i][j|(1<<k)],dp[i][j]+a[k]); 让第i个背包装下最小的,使空间充分利用。如果装不下该物体,就将dp[i+1][j|(1<<k)]=min(dp[i][j|(1<<k)],a[k]);因为是前推后,每次只能溢出一个,故完全可以直接用a[k]来修改dp[i+1][j|(1<<k)]的情况,使得将全部物体装入这些背包,且前几个背包得到充分利用。

#include<stdio.h>
#include<cmath>
#include<algorithm>
# include<iostream>
# define INF 0x3f3f3f3f
# define LINF 0x3f3f3f3f3f3f3f3f
using namespace std;

int a[20];
int dp[20][1<<20];

int main()
{
   int n,v;
   cin>>n>>v;
   int up=(1<<n)-1;

   for(int i=0;i<n;i++)
   {
       cin>>a[i];
   }

   for(int i=0;i<n;i++)
   {
       for(int j=0;j<=up;j++)
       {
           dp[i][j]=INF;
       }
   }

   for(int i=0;i<n;i++)
   {
       dp[1][1<<i]=a[i];

   }
   
   for(int i=0;i<n;i++)
   {
       for(int j=0;j<=up;j++)
       {
           if(dp[i][j]!=INF)
           {
               for(int k=0;k<n;k++)
               {
                   if((j&(1<<k))!=0)
                    continue;

                   if(dp[i][j]+a[k]<=v)
                   {
                       dp[i][j|(1<<k)]=min(dp[i][j|(1<<k)],dp[i][j]+a[k]);
                   }
                   else
                   {
                       dp[i+1][j|(1<<k)]=min(dp[i][j|(1<<k)],a[k]);

                   }
               }
           }
       }
   }


   for(int i=0;i<=n;i++)
   {
       if(dp[i][up]!=INF)
       {
           cout<<i;

           return 0;
       }
   }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秦三码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值