码迷,mamicode.com
首页 > 其他好文 > 详细

一本通1601【例 5】Banknotes

时间:2019-02-13 22:46:54      阅读:205      评论:0      收藏:0      [点我收藏+]

标签:note   return   msu   input   break   bsp   接下来   for   clu   

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
*/
单调队列优化

一本通1601【例 5】Banknotes

标签:note   return   msu   input   break   bsp   接下来   for   clu   

原文地址:https://www.cnblogs.com/gaojunonly1/p/10371987.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!