题解 P3423 【[POI2005]BAN-Bank Notes】

· · 题解

看到这一道题其实很容易就看出了第一问的做法就是多重背包,然后第二问要求输出方案,那么我们很容易就会想到DP要输出方案,很简单的一个方法就是保存当前的状态从哪里转移过来的,然后倒着输出就可以了,就做一个数组d,来保存怎么转移的,很多的dp输出方案都可以用类似的方法输出,不知道的同学可以试一下。

其中d[i][j]表示状态为f[i][j]时,是由f[i-1][d[i][j]]转移过来的
void print(int x,int y){
    if(!x)return;
    print(x-1,d[x][y]);
    printf("%d ",(y-d[x][y])/w[x]);
}

普通的直接多重背包对于这一道题明显会超时,所以我们应该使用优化算法。多重背包一共两种优化(我所知道的),二进制优化和单调队列优化。一般来说用二进制优化是比单调队列快一点的,因为单调队列常数比较大,但如果用二进制优化的话个人感觉不怎么方便统计如何转移过来的,所以我在这一题选择了单调队列优化,使用单调队列优化的话统计如何转移就可以非常简单做出来了。如果你不懂如何单调队列优化我推荐一篇博客,个人感觉讲得不错。

https://blog.csdn.net/sinat_34943123/article/details/52857327

这是我昨天晚上看到这题,当场学单调队列优化真正把我看懂的一篇博客。也感谢这一位dalao的博客,没有这篇博客我肯定过不了这题

代码如下

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<vector>
#include<deque>
#include<set>
#include<map>
#define N 20005
using namespace std;
struct Node{
    int x,y;
}q1[N],q2[N];
int n,m,w[N],c[N],f[N],d[205][N];
int head1,head2,tail1,tail2;
void print(int x,int y){//打印方案 
    if(!x)return;
    print(x-1,d[x][y]);
    printf("%d ",(y-d[x][y])/w[x]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]);
    for(int i=1;i<=n;i++)   
        scanf("%d",&c[i]);
    scanf("%d",&m);
    memset(f,0x3f,sizeof(f));
    f[0]=0;
    for(int i=1;i<=n;i++){//单调队列优化背包 
        for(int j=0;j<w[i];j++){
            head1=head2=1;
            tail1=tail2=0;
            for(int k=j,cnt=0;k<=m;cnt++,k+=w[i]){
                int xx=k/w[i];
                if(tail1-head1==c[i]){
                    if(q1[head1].x==q2[head2].x)
                        head2++;
                    head1++;
                }
                int t=f[k]-cnt;
                q1[++tail1].x=t,q1[tail1].y=k;
                while(head2<=tail2&&t<q2[tail2].x)
                    tail2--;
                q2[++tail2].x=t,q2[tail2].y=k;
                f[k]=q2[head2].x+cnt;
                d[i][k]=q2[head2].y;
            }
        }
    }
    printf("%d\n",f[m]);
    print(n,m);
    return 0;
}

PS:

1.本来我想用单调队列后,想直接看题解怎么做的单调队列,以此来学习,但题解里只有二进制优化的做法,我又没怎么看懂,就自己学,学了写这篇题解帮助一下和我一样的同学。

2.个人认为讲得比较清楚,但是平常不管是文化课还是竞赛给别人讲题基本别人听不懂。若有不懂可以私聊,我尽量回复。

3.祝NOIP2018 RP++