Cows in a Skyscraper G/洛谷P3052 题解

这道题可以用一种十分巧妙的状压做,并且很好写。

注意到牛数小于18,我们用一个二进制串表示状态,一位为1表示这只牛选过了,0表示没选过。

用 $f$ 和 $g$ 两个数组,每个状态分别表示 到达该状态需要的最少组数 以及 该状态最优分组中和最小一组的和。我们据此放入新的牛,此时显然更易放入和最小的组,也更容易减少分组数量。

可以发现,这种类似贪心的dp方法却会避免贪心时的错误,因为dp当前状态会充分地考虑到通过其每一个子集的最优子结构转移:

6 8
2 3 4 4 5 6


面对这组数据,贪心会分成 2 3|4 4|5|6 ,答案却应是 2 6|3 5|4 4,因为贪心只能从前往后找,而dp中每一种状态时每一只新加入的牛都会来尝试更新它,从 {2,6} 开始的转移便为我们提供了正确的方向。

#include<bits/stdc++.h>
#define N 300009
#define M 500009
#define INF 0x3f3f3f3f
#define mod 998244353
using namespace std;
typedef long long ll;
typedef long double ldb;
typedef pair<int,int> pii;
int n,m,t,w;
int f[N],g[N],v[29];
int main(){
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  cin>>n>>w;
  for(int i=0;i<n;i++)cin>>v[i];
  memset(f,INF,sizeof(f));
  memset(g,INF,sizeof(g));
  f[0]=1,g[0]=0;
  for(int i=0;i<(1<<n);i++){
    for(int j=0;j<n;j++){
      if(!(i&(1<<j))){
        if(g[i]+v[j]<=w&&f[i|(1<<j)]>=f[i]){
          f[i|(1<<j)]=f[i];
          g[i|(1<<j)]=min(g[i|(1<<j)],g[i]+v[j]);
        }
        if(g[i]+v[j]>w&&f[i|(1<<j)]>=f[i]+1){
          f[i|(1<<j)]=f[i]+1;
          g[i|(1<<j)]=min(g[i|(1<<j)],v[j]);
        }
      }
    }
  }
  cout<<f[(1<<n)-1]<<endl;
  return 0;
}

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值