题目:https://ac.nowcoder.com/acm/problem/21668
题目描述:
一个人如果在他很小的时候就自己赚过钱,他的一生都会过得非常节俭,因为他知道财富来之不易.
作为一个勤俭节约的好孩子,牛牛决定在生活中践行这一原则.
有一天他想去商店买一些球来玩,他发现商店里有n个盒子,每个盒子外面有一张标签告诉你有ai个红球,bi个蓝球,需要ci的钱购买
但是由于店主是一个粗心的人,他告诉你每个盒子球的总量是符合标签的说明的,但是具体的种类可能会有如下偏差,比如可能有
(ai+1 ,bi-1),(ai, bi), (ai-1, bi+1)三种可能
牛牛 想要买至少K个同颜色的球,但是他又不想浪费钱.
帮他算算最少花多少钱买盒子能够使得至少会有K个球是同色的
解题思路
典型的背包01问题
需要注意的是有可能有(-1~1)的偏差,所以我们在dp时都考虑最差的情况,考虑都只有ai-1和bi-1个球
因此我们设dp[j]为买j个球最少需要花的钱
1.dp关系为 dp[j]=min(dp[j],dp[j-(a[i]-1)+c[i]])//j>=a[i]-1,a[i]为当前这个箱子i个球,dp[j]为当前总数j个球 ,当前总数球价值=当前箱子i个球的价值+之前dp算的j-i个球的价值,因为时考虑最差情况所以时是-(i-1)
2.注意还有一种情况(看了别人的答案才明白的),就是不管你抓什么球,只要抓的球总数>=2k-1 就一定会有一种球的颜色的个数>=k(因为平均情况为k 和k-1,所以肯定会有>=k)
此时dp关系为 dp[j]=min(dp[j],dp[j]-(a[i]+b[i])+c[i])
因为C语言里没有取最小值的函数 ,不好用地址来写,所以直接用别人的C++代码,思路是一样的
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e4+100;
const int inf=0x3f3f3f3f;
int a[55],b[55],c[55],dp[maxn];
int main()
{
int n,k,ans,v=2e4-2;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
memset(dp,inf,sizeof(dp));
dp[0]=0;ans=inf;
for(int i=1;i<=n;i++)
for(int j=v;j>=(a[i]-1);j--)
dp[j]=min(dp[j],dp[j-(a[i]-1)]+c[i]);
for(int i=k;i<=v;i++) ans=min(ans,dp[i]);
memset(dp,inf,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
for(int j=v;j>=(b[i]-1);j--)
dp[j]=min(dp[j],dp[j-(b[i]-1)]+c[i]);
for(int i=k;i<=v;i++) ans=min(ans,dp[i]);
memset(dp,inf,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
for(int j=v;j>=(a[i]+b[i]);j--)
dp[j]=min(dp[j],dp[j-(a[i]+b[i])]+c[i]);
for(int i=2*k-1;i<=v;i++) ans=min(ans,dp[i]);
if(ans==inf) puts("-1");
else printf("%d\n",ans);
return 0;
}
10000小时计划
90h
淦 放纵了自己一晚上 晚上十一点才开始为了完成任务而去刷一道题哎