专栏文章

P3092 [USACO13NOV] No Change G 题解

P3092题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miqbsuv9
此快照首次捕获于
2025/12/04 02:13
3 个月前
此快照最后确认于
2025/12/04 02:13
3 个月前
查看原文
状压dpdp练习题
注意到kk很小,显然要枚举kk的状态,但还不确定用什么枚举方式。题目所说一个硬币只能用一次且不找零,所以多个硬币组合起来表示没有意义。至于为什么不能直接贪,因为用大面值去买贵物不一定更优,所以有问题。
由错误的贪心看出了一个需要解决的问题,硬币使用顺序不同对答案产生了不同影响,加上kk小,那就是经典的状压dpdp
那自然是想到dpdp了,可以考虑枚举状态ii每个硬币是否使用过。那我们发现了,状态已经表示了代价,不需要dpdp他。题目中要求 顺序 买完n个物品,所以dpdp值表示的自然是每个状态下最多能顺序买到哪个物品,这样dpdp转移就能解决硬币使用顺序的问题(每种顺序都经过最优化了)。
fi:f_{i}: 硬币使用情况为ii下,最多顺序买到哪个物品,并预处理di,jd_{i,j}表示用硬币jj从第i+1i+1个物品开始买能买到哪个物品,复杂度O(nklogn)O(nk\log n),加了个二分。
转移:fi=maxj[0,k1](2j&i)dfi2j,j+1 ;{f_{i}=\max_{j\in [0,k-1]\cap (2^j\& i) }{d_{f_{i\oplus2^j},j+1}}~};
代码(很丑):
CPP
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int K=20,N=100010;
ll ans=-1,sum[N],s;
int n,k,c[N],a[K],d[N][K],f[1<<K];
int find(int l,int r,int x)
{
    int ans=0,bg=l;
    while(l<=r){
        int mid=(l+r)/2;
        if(sum[mid]-sum[bg]<=x)
            ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
int main(){
    scanf("%d%d",&k,&n);
    for(int i=1;i<=k;i++)
        scanf("%d",&a[i]),s+=a[i];
    sort(a+1,a+1+k);
    for(int j=1;j<=k;j++)
       d[n][j]=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&c[i]);
        sum[i]=sum[i-1]+c[i];
    }
    for(int i=0;i<n;i++)
        for(int j=1;j<=k;j++)
            d[i][j]=find(i,n,a[j]);
    for(int i=0;i<(1<<k);i++)
    {
        ll nows=0;
        for(int j=0;j<k;j++)
        {
            int b=(i>>j)&1;
            if(b)f[i]=max(f[i],d[f[i^(1<<j)]][j+1]),nows+=a[j+1];
        }
        if(f[i]==n)ans=max(ans,s-nows);
    }
    printf("%lld\n",ans);
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...