Coins

15 篇文章 0 订阅

题目大意:

给定N个硬币,Ai表示第i个硬币的价值,Ci表示第i个硬币的数量。若从中选出若干个硬币把面值相加,设结果为S,则称“面值S能被拼成”。求1——M之间能拼成的面值有多少个。

解题思路:

dp
本题询问的是“可行性”(面值是否能被拼成)
设fj表示能拼成面值j,用直接拆分法直接求解会超时,我们发现:
前i-1中硬币就能拼成j,即在第i阶段开始前,变量F[j]=true。
使用了第i种硬币,即在第i阶段的递推中,发现f[j-a[i]]=true,从而变量F[j]变为true
我们可以考虑一种贪心策略:设F[j]表示f[j] 在阶段i时为true至少需要用多少枚第一种硬币,并且尽量选择第一类情况。也就是说,在f[j-a[i]]为true时,若果f[j]已经为true,则不执行Dp的转移,并令F[j]=F[j-a[i]]+1

Accepted code:

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<vector>
#include<cstdio>
#include<cctype>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define gc c=getchar()
using namespace std;
int n,m,Ans,a[100001],c[100001];
inline void read(int &f) {
    f=0; int ag=1; char gc;
    while(!isdigit(c)) {if (c=='-') ag=-1; gc;}
    while(isdigit(c)){f=(f<<3)+(f<<1)+c-48;gc;}
    f*=ag; return;
}

bool init() {
    read(n);read(m);
    if (n&&m) {
        for (int i=1;i<=n;i++) read(a[i]);
        for (int i=1;i<=n;i++) read(c[i]);
    } else return false;
    return true;
}

void Dp() {
    int F[m+1];bool f[m+1]; Ans=0;
    memset(f,0,sizeof(f));
    f[0]=true;
    for (int i=1;i<=n;i++) {
        for (int j=0;j<=m;j++) F[j]=0;
        for (int j=a[i];j<=m;j++)
            if (!f[j]&&f[j-a[i]]&&F[j-a[i]]<c[i]) {
                f[j]=true; F[j]=F[j-a[i]]+1,Ans++;
            }
    }
    return;
}

void write(int x) {
    if (x>9) write(x/10); putchar(x%10+48); return;
}
void writeln(int x) {
    write(x); putchar('\n');
}

int main() {
    while (init()) {
        Dp();
        writeln(Ans);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值