[POI2005]BAN-Bank Notes

[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;
}
posted @ 2020-09-28 20:52  BILL666  阅读(137)  评论(0编辑  收藏  举报