POJ3260 Solution

题目链接

题解

混合背包呐,约翰所付的钱为多重背包,商店的找零为完全背包。而最大容量,也就是约翰的最大花费为\(T+maxn^2\)

证明:若约翰花费\(>T+maxn^2\),则找零的硬币数\(>maxn\),而约翰除必须付的\(T\)元钱外支付的硬币数也\(\ge maxn\)。将这两类硬币分别排序,可以得到\(\ge maxn\)种前缀和。根据抽屉原理,两者都一定存在一个子序列使其之和可以被\(maxn\)整除。将该子序列全部换为面值为\(maxn\)的硬币,重复的部分可以两个人都不花费,与硬币数最少的条件矛盾。

分别dp后枚举约翰所付钱数即可。

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int T=10000,N=100,inf=0x3f3f3f3f;
int w[10*N],v[N],c[N],v2[10*N],dp1[3*T],dp2[3*T];
inline int read()
{
	int s=0,w=1; char ch=getchar();
	while(ch<'0' || ch>'9') {if(ch=='-') w=-1; ch=getchar();}
	while(ch>='0' && ch<='9') s=s*10+ch-'0',ch=getchar();
	return s*w;
}
int main()
{
	int n=read(),t=read(),ma=0,cnt=0;
	for(int i=1;i<=n;i++) v[i]=read(),ma=max(ma,v[i]);
	for(int i=1;i<=n;i++) c[i]=read();
	int x,qwq=t+ma*ma,ans=inf;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;c[i]>0;j<<=1)
		{
			x=min(c[i],j),c[i]-=x;
			v2[++cnt]=v[i]*x,w[cnt]=x;
		}
	}
	memset(dp1,0x3f,sizeof(dp1)),dp1[0]=0;
	memset(dp2,0x3f,sizeof(dp2)),dp2[0]=0;
	for(int i=1;i<=cnt;i++)
		for(int j=qwq;j>=v2[i];j--) dp1[j]=min(dp1[j],dp1[j-v2[i]]+w[i]);
	for(int i=1;i<=n;i++)
		for(int j=v[i];j<=qwq;j++) dp2[j]=min(dp2[j],dp2[j-v[i]]+1);
	for(int i=t;i<=qwq;i++) ans=min(ans,dp1[i]+dp2[i-t]);
	if(ans>=inf) printf("-1");
	else printf("%d",ans);
	return 0;
}
posted @ 2021-07-10 10:55  violet_holmes  阅读(45)  评论(1编辑  收藏  举报