题意:有n个品种的物品,每个物品的价格为c[ i ],每次你可以从你拥有的物品中挑取部分去商店去兑换。兑换有如下限制:
(1)你拿去兑换的物品(不妨设拿去兑换的物品集合为s),都要换成集合s所没有的。比如(a,b)--->(v,a)就是不合法的
(2)你拿去兑换的物品集s1里的价值和cost1与你兑换回来的物品集s2里的价值和cost2需要满足:cost1+d>=cost2
每天你只能去兑换一次。
问通过兑换可以获得的物品集价值和的最大值以及所需的兑换天数
思路:我们用dp记录当前价值和是否可达。然后每次贪心找满足增值<=d可以到达的最大状态。不妨设当前状态为a,可到达的最大状
态一定满足 a+d>=b,那么一定可以从a状态转移到b。
(1)如果a,b没有交集,那么直接把所有商品拿去兑换成b状态的商品。
(2)如果a,b有交集,那么之需要不把相交的部分拿去兑换就好。
详见代码:
// file name: codeforces364B.cpp //
// author: kereo //
// create time: 2014年08月24日 星期日 17时43分27秒 //
//***********************************//
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<set>
#include<map>
#include<vector>
#include<stack>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=600000+100;
#define L(x) (x<<1)
#define R(x) (x<<1|1)
int n,d;
int dp[MAXN];
int main()
{
while(~scanf("%d%d",&n,&d)){
int sum=0;
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x); sum+=x;
for(int j=sum;j>=x;j--) if(dp[j-x])
dp[j]=1;
}
int st=0,ans=0;
while(1){
st+=d;//这一次的状态上限
int pos=d;
for(int i=0;i<d;i++) if(dp[st-i]){ //该次最大可到达的状态
pos=i; break;
}
if(pos == d) {
st-=d; break;
}
st-=pos;
ans++;
}
printf("%d %d\n",st,ans);
}
return 0;
}