【貪心+01揹包】洛谷P2393kkksc03臨時抱佛腳
NOIOL第三場pj竟然混了240分進了前25%,蒟蒻深感自己DP的薄弱(T3竟然連50分都拿不到),開始在洛谷刷DP,做揹包的時候看到的這道題。沒想到一道橙題卡了半天
分析
題目說了要先做完一個題目集再做後面的,所以只要分別求四個子集的最優解,把它們相加就可以了。
對於一個子集的最優解,我們就是把總時間分配到左腦和右腦,然後讓兩者較大的值儘可能小,即讓他們儘可能接近。
這裏運用了貪心的思想,我們做完一個子集的題目,花費的時間是左腦和右腦花費時間大的那個,比如說左腦3,右腦5,我花費的時間就是5,那麼讓大的那個儘可能小也就是平均分就是最優解。
如果你做過一道小s的遊戲機電池這道貪心,你會發現到這裏好像和那道題思路差不多(要是沒看過就忽略掉),但是注意,這道題裏開始做一道題目,直到做完,我們才能讓這一半大腦去做下一道題,這是和那道電池題目的不同之處,這道題可能我們無法剛好分配出一半的時間給左腦,一半的時間給右腦,因爲一道題目不能分割。舉個例子,如果3道題分別要花費1、2、5的時間,我無論如何也不能分配出4這個時間吧,但是我可以讓一半的大腦在不超過4的情況下,儘可能的大(讓1、3在左腦)。這樣,左腦和右腦花費時間較多的那個就是所需的最少時間(其實理解了就不難明白,認真看幾遍就明白了)。
經過上面的轉換,我們發現求左半邊大腦所需的時間就是在總時間不超過sum/2的情況下,選取若干道題目,讓時間儘可能的大。 (這就是道0/1揹包的裸題)求出左半邊,右半邊用sum減去左半邊就得出了,最後求左半邊和右半邊的最大值,就是這個子集所花費的時間。
最後不要忘了有4個子集,都要算出來啊
最後蒟蒻貼上AC代碼(話說這題是蒟蒻在luogu的第100道AC欸):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int s1,s2,s3,s4,minn,sum;
int t[25],f[25][620];
void dp(int n,int maxn){
memset(f,0,sizeof f);
//在不超過maxn的情況下讓價值儘可能大
for(int i=1;i<=n;i++){
for(int j=0;j<=maxn;j++){
if(t[i]>j)f[i][j]=f[i-1][j];
else f[i][j]=max(f[i-1][j],f[i-1][j-t[i]]+t[i]);
}
}
minn += (sum-f[n][maxn]);
sum = 0;
}
int main(){
ios::sync_with_stdio(false);
cin>>s1>>s2>>s3>>s4;
for(int i=1;i<=s1;i++){
cin>>t[i];
sum+=t[i];
}
dp(s1,sum/2);
for(int i=1;i<=s2;i++){
cin>>t[i];
sum+=t[i];
}
dp(s2,sum/2);
for(int i=1;i<=s3;i++){
cin>>t[i];
sum+=t[i];
}
dp(s3,sum/2);
for(int i=1;i<=s4;i++){
cin>>t[i];
sum+=t[i];
}
dp(s4,sum/2);
cout<<minn<<endl;
return 0;
}
今天的文就到這裏了(話說上篇差分的都沒人看啊,不會是我寫的太差了吧)