題目描述
重慶八中在\(80\)週年校慶的時候獲捐\(n\)個杯子, 每個杯子有兩個屬性:一個是已裝水量\(ai\),一個是可裝水量\(bi\)(\(ai <= bi\))。
從一個杯子向另一個杯子倒\(x\)單位體積的水需要花費的時間是\(x\)秒。 現在用\(n\) 個杯子中的\(k\)個來裝所有的水, 求最小的\(k\), 以及最少花費的時間 \(t\)。
第一行:一個正整數\(n(1 <= n <= 100)\),代表杯子的個數。
第二行:\(n\)個正整數\(a1, a2, a3, a4 , … an(1 <= ai <= 100)\),\(ai\)表示第i 個杯子已裝水量。
第三行:\(n\)個正整數\(b1, b2, b3, b4 , … bn(1 <= bi <= 100)\),\(bi\)表示第\(i\)個杯子可裝水量。
保證對於任意一個杯子,\(ai <= bi\)
思路
這道題就是一道二維揹包問題。
我們可以預處理出\(k\)的值。
然後用\(DP\)來求出\(t\)。我們令\(f[i][j][k]\)表示處理到了第\(i\)個杯子,一共選了\(k\)個杯子,可裝水為\(j\)時的最大裝水量。
那我們的遞推式為:
f[i][j][k]=max(f[i-1][j][k],f[i-1][j-a[i].bi][k-1]+a[i].ai);
//f[i-1][j][k]為不選這個杯子
//f[i-1][j-a[i].bi][k-1]+a[i].ai為選這個杯子
就像\(01\)揹包一樣,我們可以把\(i\)這一位給省掉。
遞推式就變為:
f[j][k]=max(f[j][k],f[j-a[i].bi][k-1]+a[i].ai);
程式碼
#include <bits/stdc++.h>
using namespace std;
int n,sum,all_water,f[10005][105],ans,need;
struct node{ int ai,bi; }a[105];
bool cmp(const node &a,const node &b) {
if(a.bi==b.bi) return a.ai>b.ai;
return a.bi>b.bi;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i].ai),sum+=a[i].ai;
for(int i=1;i<=n;i++) scanf("%d",&a[i].bi),all_water+=a[i].bi;
sort(a+1,a+n+1,cmp);
int value=0;
for(int i=1;i<=n;i++) {//求k
value+=a[i].bi,need++;
if(value>=sum) break;
}
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++) {
for(int j=all_water;j>=a[i].bi;j--) {
for(int k=1;k<=need;k++)
f[j][k]=max(f[j][k],f[j-a[i].bi][k-1]+a[i].ai);
}
}
for(int i=sum;i<=all_water;i++) ans=max(ans,f[i][need]);
printf("%d %d",need,sum-ans);
return 0;
}