卧薪尝胆,厚积薄发。
POI2005 BAN-Bank notes
Date: Thu Nov 01 15:51:01 CST 2018
In Category:
NoCategory
Description:
有
$n$
种面值的硬币,面值分别为
$b_1, b_2,\dots,b_n$
。每种硬币有数量限制,想要凑出面值
$k$
求最少要用多少个硬币。
$1\leqslant n\leqslant 200$
Solution:
裸的多重背包,但是卡空间,于是二进制拆分,记录一下每个拆开的物品属于哪个原来的物品,然后这样记录方案就不用记
$pre$
了,只用记录
$used[i][j]$
表示第
$i$
个物品在第
$j$
个容积是否被使用,这样只用
$bool$
数组就可以了。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n;
#define MAXN 210
int a[MAXN];
#define MAXO 2010
int b[MAXO],c[MAXO],tot = 0;
int bel[MAXO];
#define MAX 20010
int f[MAX];
bool used[MAXO][MAX];
int k;
int ans[MAXN];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
int w;
for(int i = 1;i <= n;++i)
{
scanf("%d",&w);
for(int k = 0;(1 << k) <= w;++k)
{
++tot;
b[tot] = (1 << k) * a[i];w -= (1 << k);
c[tot] = (1 << k);bel[tot] = i;
}
if(w != 0)
{
++tot;
b[tot] = w * a[i];c[tot] = w;
bel[tot] = i;
}
}
memset(f,0x3f,sizeof(f));
f[0] = 0;
for(int i = 1;i <= tot;++i)
{
for(int j = MAX;j >= b[i];--j)
{
if(f[j - b[i]] + c[i] < f[j])
{
f[j] = f[j - b[i]] + c[i];
used[i][j] = true;
}
}
}
scanf("%d",&k);
cout << f[k] << endl;
int cur = k;
for(int i = tot;i >= 1;--i)
{
if(used[i][cur])
{
ans[bel[i]] += c[i];
cur -= b[i];
}
}
for(int i = 1;i <= n;++i)printf("%d ",ans[i]);
puts("");
return 0;
}
In tag:
DP-背包
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡