题意:
给出n个优惠卷,当你目前需要花费的钱m>va[i].fi的时候,你可以用这个优惠卷,每个优惠卷只能用一次。求最小花费多少可以买到商品。
思考:
很明显,如果只拿大的肯定不行,这种题目一看就知道需要排序。但是怎么排序呢?这里就想到国王的游戏这个题目,就是两个物品,假设先用i再用j是比先用j再用i好的,然后把式子列出来看看到底是怎么样的。m-va[i].se>=va[j].fi && m-va[j].se<va[i].fi,整理一下得va[i].fi-va[i].se>va[j].fi-va[j].se。所以就按这个排序,其实你感性的理解一下,肯定选那种差距较大的卷优先,然后再放差距小的卷,就像瓶子里放石头,肯定先放大石头再放小石头才能把瓶子装的最满。这里值得注意的是不能直接贪心遍历一遍,因为这是不对的,仔细看看感觉想背包,又一想感觉这就是状态dp,所以枚举遍历一遍,看看最小的状态可行值是多少即可。其实dp状态就定义为:用到第i个优惠卷,j价钱是否可以得到。转移的话就是要么用要么不用。发现,每次用的都是上一层的i,故可以优化到一维,但是要注意顺序,优化后要记得正序转移,也就是从x来转移,这个x必须是上一层的。
代码:
一维:
int T,n,m;
PII va[N];
int dp[N];
bool cmp(PII A,PII B)
{
return A.fi-A.se>B.fi-B.se;
}
signed main()
{
IOS;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>va[i].fi>>va[i].se;
sort(va+1,va+1+n,cmp);
dp[m] = 1;
for(int i=1;i<=n;i++)
{
for(int j=va[i].fi;j<=m;j++) //每个卷只能用一次
dp[j-va[i].se] |= dp[j]; //所以必须从前往后,如果从后往前,那么一个卷可能用两次
} //比如90可以从100过来,但是你倒叙j,所以80又可以从刚才的90过来,所以必须正序。
for(int i=0;i<=m;i++)
{
if(dp[i])
{
cout<<i;
break;
}
}
return 0;
}
二维:
int T,n,m,k;
PII va[1005];
int dp[1005][10005]; //用到第i个优惠卷,j价钱是否可以得到
bool cmp(PII A,PII B)
{
return A.se-A.fi<B.se-B.fi;
}
signed main()
{
IOS;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>va[i].fi>>va[i].se;
sort(va+1,va+1+n,cmp);
dp[0][m] = 1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j] |= dp[i-1][j]; //如果不用
if(j>=va[i].fi) dp[i][j-va[i].se] |= dp[i-1][j]; //如果用
}
}
for(int i=0;i<=m;i++)
{
if(dp[n][i])
{
cout<<i;
break;
}
}
return 0;
}
总结:
一定要好好考虑,怎么样才是最佳,该怎么枚举才能是正确的方案。多思考一下。