[POI2005]BAN-Bank Notes(单调队列优化多重背包)

题目描述

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

输入输出格式

输入格式:

 

第一行一个数 n;

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

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

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

输出格式:

The first line of the standard output should contain one integer denoting the minimal total number of bank notes sufficient to pay the sum off kk. The second line should contain nn integers, separated by single spaces, denoting the numbers of notes of subsequent denominations used to pay off the sum kk. If there are many solutions your programme should write any of them.

 

输入输出样例

输入样例#1: 
3
2 3 5
2 2 1
10
输出样例#1: 
3
1 1 1

n<=200,k,bi,ci<=20000

思路:
很显然,这是一道明显的多重背包问题,暴力的时间复杂度为O(sigema(ci)*k) T的一批
然后我们想到用单调队列优化,如果j块钱需要第i个钱币x个去支付,那么他j一定是由j-b[i]*x转移过来的
这样一来,我们就可以想到单调队列的做法,我们枚举%bi的每个余数,这个余数肯定不能被这个钱币表示出来
设当前的价值j=b[i]*t+mod;
然后我们枚举不用k个i种钱币的最小值,即寻找当k<t时的t-k+dp[j-b[i]*k]最小
很显然我们用单调队列维护一个
-k+dp[j-b[i]*k]就好了
那个每个钱币有多少个也非常简单
希望可以自己想一想

上代码


 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #define rep(i,a,b) for(long long i=a;i<=b;i++)
 7 using namespace std;
 8 int read(){
 9     int x=0,f=1;
10     char c=getchar();
11     while('0'>c || c>'9'){if(c=='-') f=-1; c=getchar();}
12     while('0'<=c && c<='9'){x=(x<<1)+(x<<3)+c-'0'; c=getchar();}
13     return x*f;
14 }
15 deque<pair<int,int> > Q;
16 int n,k,b[205],c[205],dp[20500],t,x,fa[205][20500]; //fa[i][j]表示面值为j的钱经过x个面值为bi的硬币后剩余的钱 
17 void print(int i,int j){
18     if(i==0) return;
19     print(i-1,fa[i][j]);
20     printf("%d ",(j-fa[i][j])/b[i]);
21 }
22 int main(){
23     n=read();
24     rep(i,1,n) b[i]=read();
25     rep(i,1,n) c[i]=read();
26     k=read();
27     memset(dp,127,sizeof(dp)); dp[0]=0;
28     rep(i,1,n){
29         rep(j,0,b[i]-1){
30             t=-1;
31             while(!Q.empty()) Q.pop_back();
32             while(1){
33                 t++;
34                 if(t*b[i]+j>k) break;
35                 x=t*b[i]+j;
36                 while(!Q.empty() && Q.front().first+c[i]<t) Q.pop_front();
37                 while(!Q.empty() && Q.back().second>=dp[x]-t) Q.pop_back();
38                 Q.push_back(make_pair(t,dp[x]-t));
39                 dp[x]=Q.front().second+t;
40                 fa[i][x]=x-(t-Q.front().first)*b[i];
41             }
42         }
43     }
44     printf("%d\n",dp[k]);
45     print(n,k);
46     return 0;
47 }

 



posted @ 2018-11-30 14:57  niolle  阅读(419)  评论(0编辑  收藏  举报