洛谷P2393 kkksc03臨時抱佛腳

【貪心+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;
}

今天的文就到這裏了(話說上篇差分的都沒人看啊,不會是我寫的太差了吧

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章