一本通1601【例 5】Banknotes

1601:【例 5】Banknotes

时间限制: 1000 ms         内存限制: 524288 KB

【题目描述】

原题来自:POI 2005

Byteotian Bit Bank (BBB) 拥有一套先进的货币系统,这个系统一共有 n 种面值的硬币,面值分别为 b1,b2,,bn。但是每种硬币有数量限制,现在我们想要凑出面值 k,求最少要用多少个硬币。

【输入】

第一行一个数 n;

接下来一行 n 个整数 b1,b2,,bn

第三行 n 个整数 c1,c2,,cn ,表示每种硬币的个数;

最后一行一个数 k,表示要凑的面值数。

【输出】

第一行一个数表示最少需要付的硬币数。

【输入样例】

3
2 3 5
2 2 1
10

【输出样例】

3

【提示】

数据范围与提示:

对于全部数据,1n200,1b1<b2<<bn2×104,1ci,k2×104 。

 

sol:比较裸的完全背包??可以有两种优化:

二进制优化太水不说了

#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=' ';
    while(!isdigit(ch))
    {
        f|=(ch=='-');
        ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x<10)
    {
        putchar(x+'0');
        return;
    }
    write(x/10);
    putchar((x%10)+'0');
    return;
}
inline void writeln(ll x)
{
    write(x);
    putchar('\n');
    return;
}
#define W(x) write(x),putchar(' ')
#define Wl(x) writeln(x)
const int N=20005,M=2000005;
int n,m,B[N],C[N],dp[M];
int main()
{
    int i,j,k;
    R(n);
    for(i=1;i<=n;i++) R(B[i]);
    for(i=1;i<=n;i++) R(C[i]);
    R(m);
    memset(dp,63,sizeof dp);
    dp[0]=0;
    for(i=1;i<=n;i++)
    {
        for(j=1;j<=C[i]&&j*B[i]<=m;j<<=1)
        {
            for(k=m;k>=j*B[i];k--) dp[k]=min(dp[k],dp[k-j*B[i]]+j);
        }
    }
    Wl(dp[m]);
    return 0;
}
/*
input
3
2 3 5
2 2 1
10
output
3

input
10
6 17 111 249 250 495 496 497 498 499
100 100 100 100 1 100 100 100 100 100
500
output
6

input
3
300 700 4800
10000 10000 10000
5000
output
10
*/
二进制优化

单调队列优化:对于模Bi相同的几个权值之间的dp转移,可以用单调队列优化,令权值V=j+k*Bi,dp[V]=min(dp[V],dp[j+k'*Bi]+k-k‘),所以可以用dp[j+k*Bi]-k最小为队首的单调队列来优化成n*m,(细节:为了防止被反复统计,应该先插入当前节点再更新当前节点的dp值)

#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=' ';
    while(!isdigit(ch))
    {
        f|=(ch=='-');
        ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48);
        ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x<10)
    {
        putchar(x+'0');
        return;
    }
    write(x/10);
    putchar((x%10)+'0');
    return;
}
inline void writeln(ll x)
{
    write(x);
    putchar('\n');
    return;
}
#define W(x) write(x),putchar(' ')
#define Wl(x) writeln(x)
const int N=205,M=20005;
int n,m,B[N],C[N],dp[M];
struct Data
{
    int Shuz,Weiz;
}Ddq[M];
int main()
{
    int i,j,k;
    R(n);
    for(i=1;i<=n;i++) R(B[i]);
    for(i=1;i<=n;i++) R(C[i]);
    R(m);
    memset(dp,63,sizeof dp);
    dp[0]=0;
    for(i=1;i<=n;i++)
    {
        for(j=0;j<B[i];j++)
        {
            int Head=1,Tail=0;
            for(k=0;;k++)
            {
                int x=k*B[i]+j; if(x>m) break;
                while(Head<Tail&&Ddq[Head].Weiz<k-C[i]) Head++;
                while(Head<=Tail&&dp[x]-k<Ddq[Head].Shuz-Ddq[Head].Weiz) Tail--;
                Ddq[++Tail]=(Data){dp[x]-k,k};
                dp[x]=min(dp[x],Ddq[Head].Shuz+k);
            }
        }
    }
    Wl(dp[m]);
    return 0;
}
/*
input
3
2 3 5
2 2 1
10
output
3
*/
单调队列优化
posted @ 2019-02-13 21:46  yccdu  阅读(807)  评论(0编辑  收藏  举报