题目
本题为改编题。
发鸠之山,其上多柘木。有鸟焉,其状如乌,文首,白喙,赤足,名曰精卫,其名自詨。是炎帝之少女,名曰女娃。女娃游于东海,溺而不返,故为精卫。常衔西山之木石,以堙于东海。——《山海经》
精卫终于快把东海填平了!只剩下了最后的一小片区域了。同时,西山上的木石也已经不多了。精卫能把东海填平吗?
事实上,东海未填平的区域还需要至少体积为 v 的木石才可以填平,而西山上的木石还剩下 n 块,每块的体积和把它衔到东海需要的体力分别为 k 和 m。精卫已经填海填了这么长时间了,她也很累了,她还剩下的体力为 c。
输入格式
输入文件的第一行是三个整数:v,n,c。
从第二行到第 n+1 行分别为每块木石的体积和把它衔到东海需要的体力。
输出格式
输出文件只有一行,如果精卫能把东海填平,则输出她把东海填平后剩下的最大的体力,否则输出 Impossible
(不带引号)。
输入输出样例
输入 #1
100 2 10
50 5
50 5
输出 #1
0
输入 #2
10 2 1
50 5
10 2
输出 #2
Impossible
说明/提示
数据范围及约定
-
对于 20%的数据,0<n≤50;
- 对于 50%的数据,0<n≤1000;
- 对于 100%的数据,0<n≤104,所有读入的数均属于 [0,104],最后答案不大于 c。
解题思路
一.
这道题让我们算精卫是否能把东海填平,并求能填平的最小体力。我们可以看到体力是本题的要点,那我们不妨设数组f[i]表示消耗i的体力可以搬运的最多石子。然后套用01背包模板,算出每一个f的值
最后我们就可以判断用全部的体力(也就是i==c)是否能填平,如果不能,就输出“Impossibel”,不然就一直向下找,直到找到刚好够填平时消耗的体力,用总体力一减,就是答案了,总代码只有20行,是不是很棒呢?
二.
这一看首先想到的就是01背包是没有疑问的,然后我来吧题目化简成01背包的特征,你有容积为c的背包,有n个物品,每一块物体的价值和体积分别为k和m,用dp[i][j]表示前i个物品,用容积为j的背包最大的价值,那么:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-m]+k)
然后可以用滚动数组以及边读边输入的方式优化他的空间复杂度和增大代码可读性(美观),新的定义下动态规划公式:
dp[i]=max(dp[i],dp[i-m]+k)
背包写好后再进行一个判断,最大的dp如果大于v,那就有方案了,否则就输出Impossible 以上是01背包的部分,但是为什么这题难度比01背包高呢?原来题目多了一个要求,就是要求在能填满海的条件下尽量省体力,其实也不难,中间加一个判断就行了,当可以填满海的时候体力可以用一个变量ans来表示,然后不断打擂台更新,最后就是结果了。
AC
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int n,m,c,v[N],w[N],dp[N];
void solve(){
scanf("%d%d%d",&m,&n,&c);
for(int i=1;i<=n;i++){
scanf("%d%d",&w[i],&v[i]);
}
for(int i=1;i<=n;i++){
for(int j=c;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
}
}
if(dp[c]>=m){
int x=c;
while(dp[x]>=m) x--;
cout<<c-(x+1);
}
else cout<<"Impossible";
}
signed main(){
int t=1;
while(t--) solve();
return 0;
}