1. 程式人生 > >【題解】洛谷P2577 [ZJOI2005] 午餐(DP+貪心)

【題解】洛谷P2577 [ZJOI2005] 午餐(DP+貪心)

次元傳送門:洛谷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; }