【題解】洛谷P2577 [ZJOI2005] 午餐(DP+貪心)
阿新 • • 發佈:2018-11-06
次元傳送門:洛谷P2577
思路
首先貪心是必須的
我們能感性地理解出吃飯慢的必須先吃飯(結合一下生活)
因此我們可以先按吃飯時間從大到小排序
然後就能自然地想到用f[i][j][k]表示前i個人在第一個視窗排隊用了j時間 在第二個視窗排隊用了k時間
然後就自然地炸空間了
所以我們要降維
因為我們可以由第一個視窗推出第二個視窗所用時間 所以我們可以改原來的陣列為:
f[i][j]表示前i個人 在第一個視窗用了j時間 得到的所有前i個人吃完飯的最短時間
如何用第一個視窗推出第二個視窗呢?
顯而易見 第一個視窗排隊用了j時間+第二個視窗排隊用了k時間為前i個人排隊用的總時間
那麼我們可以用字首和維護一個總時間sum[i]表示前i個人用的總時間
那麼k=sum[i]-j 解決
狀態轉移方程:
f[i][j]=min(f[i][j],max(f[i-1][j-p[i].a],j+p[i].b));//第i個人在第一視窗 //f[i-1][j-p[i].a]表示第i個人排隊+吃飯的時間比第i-1個人短 所以不影響 //j+p[i].b即有影響 前i個人排隊時間+第i個人吃飯時間 f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+p[i].b));//第i個人在第二視窗 //同理 sum[i]-j=k即第二個視窗排隊總時間
程式碼
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define maxn 220 #define INF 1e9+7 struct P { int a; int b; }p[maxn]; int n; int sum[maxn]; int f[maxn][maxn*maxn]; bool cmp(P a,P b) { return a.b>b.b; }int main() { memset(f,0x3f,sizeof(f)); cin>>n; for(int i=1;i<=n;i++) cin>>p[i].a>>p[i].b; sort(p+1,p+1+n,cmp);//排序 for(int i=1;i<=n;i++) sum[i]=sum[i-1]+p[i].a;//字首和 f[0][0]=0;//初始化 for(int i=1;i<=n;i++)//列舉第幾個人 for(int j=0;j<=sum[i];j++)//列舉排隊時間 { if(j>=p[i].a) f[i][j]=min(f[i][j],max(f[i-1][j-p[i].a],j+p[i].b)); f[i][j]=min(f[i][j],max(f[i-1][j],sum[i]-j+p[i].b)); } int ans=INF; for(int i=0;i<=sum[n];i++)//查詢時間 ans=min(ans,f[n][i]); cout<<ans; }