[POI2005]BAN-Bank Notes
problem:
经典的多重背包问题。要求输出方案
data range:
正常
solution:
问题本身不难。主要在于输出方案。
我采用的是二进制优化多重背包。
如果不用任何辅助数组直接做就是这样:
void find(int x,int pos)
{
if(!x)return;
int u=0;
for(int i=pos;i;--i)
if(f[pos-1][x-a[i-1].se]+a[i-1].fi==f[pos][x])
{
u=i,cnt[a[i-1].se/a[i-1].fi]+=a[i-1].fi;
break;
}
find(x-a[u-1].se,u-1);
}
然而这道题十分不友善卡空间,全部MLE了
那么我们对f用滚动数组优化空间,然后定义(flag_{i,j})表示(f_{i,j})在此处是否发生转移
然后类似套用就可以了---
space time complexity:
time&space:(O(NMlogM))
code:
#include<bits/stdc++.h>
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
typedef pair<int,int> pii;
const int N=205,M=2e4+5;
int n,k;
int b[N],f[M],cnt[M];
bool flag[N<<4][M];
vector<pii>a;
void find(int x,int pos)
{
if(!x)return;
int u=0;
for(int i=pos;i;--i)
if(flag[i][x])
{
u=i,cnt[a[i-1].se/a[i-1].fi]+=a[i-1].fi;
break;
}
find(x-a[u-1].se,u-1);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",b+i);
for(int i=1;i<=n;++i)
{
int c;scanf("%d",&c);
for(int j=0;c;++j)
{
int cnt=1<<j;
if(c<cnt)a.pb(mp(c,b[i]*c)),c=0;
else a.pb(mp(cnt,b[i]*cnt)),c-=cnt;
}
}
scanf("%d",&k);
memset(f,120,sizeof(f));
f[0]=0;
for(int i=1;i<=a.size();++i)
for(int j=k;j>=a[i-1].se;--j)
if(f[j-a[i-1].se]+a[i-1].fi<f[j])
f[j]=f[j-a[i-1].se]+a[i-1].fi,flag[i][j]=true;
cout<<f[k]<<endl;
find(k,a.size());
for(int i=1;i<=n;++i)printf("%d ",cnt[b[i]]);
return 0;
}