金币(coin)HGOI题解

题目描述

  • 金银岛上的人使用金币,每种金币面值分别是 A1; A2; A3; : : : ; An 元。一天 Tony 决定在
    附近商店买一个非常好的表,他想在付钱的时候不要找零,但是他发现他的钱包里每种金
    币的数量分别只有 C1; C2; C3; : : : ; Cn 个。不过,Tony 知道这块表的价格不会超过 M 元金币
    (他不知道表的精确价格)。不知他的付钱方式能否实现。

  • 你的任务是帮助 Tony 算一下,在 1::M 元范围内(包括边界),他钱包中的金币可以精
    确支付多少种价格。

输入格式

  • 输入包括多组测试数据。每组测试数据的格式如下:
  • 第一行包括 2 个整数 n; M。
  • 第二行包括 2n 个整数 A1; A2; A3; : : : ; An 和 C1; C2; C3; : : : ; Cn。
  • 测试数据的最后一行有 2 个 0,这一行无需处理。

输出格式

每组测试数据输出一行,为一个整数,即能精确支付的价格种数。

数据规模与约定

  • 对于 30% 的数据,1<=N<=30; 1<=M<=1000
  • 对于 60% 的数据,1<=N<=60
  • 对于 100% 的数据,1<=n<=100; 1<=M<=10^5 1<=Ai<=105; 1<=Ci<=1000

题解

  • 由于数据量过大(如果尝试枚举复杂度为n*m*Ci)肯定会超时,就需要进行压缩。
  • 如果转化为单纯的0-1背包问题,时间复杂度任为(O)n*m*Ci,空间复杂度为m(∑Ci)。
  • 如果采用滚动数组,空间复杂度可降为m,但时间上任然会爆炸。
  • 因此需要进行二进制转换大小压缩。例:13=1+2+4+6;在前面的(1+2+4)当中就可以实现1——7的所有情况的枚举,并且可以最大化的减少重复的金额出现。
 for (int j=temp;j<temp+(int)(log(a[i].C)/log(2));j++) {
    aa[j]=a[i].A*(1<<(j-temp));
  • 是进行对于重复多次相同金额的压缩。时间复杂度降为(O)m∑log2(Ci)。

直接上代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define INF 0x7f7f7f
using namespace std;
int m,n;
void f(){
    freopen("coin.in","r",stdin);
    freopen("coin.out","w",stdout);
}
struct node{
    int A,C;
}a[101];
int dp[100001],aa[100001];
bool check(int a,int b){
    return a>b;
}
int main(){
    f();
    while(cin>>n>>m&&(n!=0&&m!=0)){
        memset(dp,-INF,sizeof(dp));//初始化无限小
        memset(aa,0,sizeof(aa));
        int sum=0,t=1,ans=0;
        for (int i=1;i<=n;i++) {
            cin>>a[i].A;
        }
        dp[0]=0;
        for (int i=1;i<=n;i++) {
            cin>>a[i].C;
            sum+=a[i].C*a[i].A;
            int temp=t,ss=0;
            for (int j=temp;j<temp+(int)(log(a[i].C)/log(2));j++) {
                aa[j]=a[i].A*(1<<(j-temp));//进行二进制左移
                ss+=1<<(j-temp);
                t++;
            }
            if(ss<a[i].C) aa[t++]=a[i].A*(a[i].C-ss);//最后多余为a[i].C-ss单独拿出放在最后
        }
        for (int i=1;i<t;i++){
            for (int j=m;j>=aa[i];j--){
                dp[j]=max(dp[j],dp[j-aa[i]]+1);//0-1背包问题
            }
        }
        for (int i=1;i<=m;i++) if(dp[i]>0) ans++;//最后统计结果
        cout<<ans<<'\n';
    }
}

猜你喜欢

转载自blog.csdn.net/qq_42037034/article/details/80039611