1. 程式人生 > 實用技巧 >暑假集訓Day 10 小烈送菜

暑假集訓Day 10 小烈送菜

題目大意

小烈一下碰碰車就被樂滿地的工作人員抓住了。作為擾亂秩序的懲罰,小烈必須去樂滿地裡的“灕江村”飯店端盤子。

服務員的工作很繁忙。他們要上菜,同時要使顧客們儘量高興。一位服務生為n個顧客上菜。這n個顧客坐成一排,小烈從一端的廚房中端出n盤菜(不要問我為什麼小烈能一下子端住2500 盤菜,他就是能)為n個顧客各上一道相同的菜。

顯然,小烈需要走一個來回,如圖:

本來,小烈可以按1,2,3,...n的順序一次給每個顧客上菜,但是,聰明的小烈通過觀察發現,每個顧客都有一個開心值 H,離廚房最近的為H1 ,然後依次為\(H_2\),\(H_3\)...\(H_n\) 。若小烈給第j位顧客上菜前剛剛為第i位顧客上菜,則第j位就會高興,產生高興指數\(W_j =H_i*H_j\)

。這樣,如果小烈按一定的方式調整上菜順序,可以得到更高的高興指數。現在小烈想知道用某一方法可達到的n位顧客高興指數之和的最大值S。因為顧客越高興,給小烈的小費越多。第一位上菜的顧客不產生高興值。

輸入格式

第一行一個整數n,顧客的數目。
第二行n個數,第i個數表示第i位顧客的開心值。各個數字用空格隔開。

輸出格式

一個數s,為高興指數的最大值。

樣例

樣例輸入

3
7 1 9

樣例輸出

72

演算法分析

  • 這個題第一眼看上去好像沒啥思路 雖然能一眼裸爆出是dp 但是狀態和轉移限制異常的多
  • 小烈要去一個來回 去的時候獲得的權值回來的時候就不能獲得了 那如果我們將一個小烈劈開(還是讓他分身吧) 現在我們有兩個小烈 一起向前走 f[i][j]表示第一個小烈走到i 第二個小烈走到j而且此時前max(i,j)個客人都已經送好菜了,所能獲得的最大值
  • 如果當前狀態是i 如何轉移到i+1呢? 因為i+1必須要由小烈a或者小烈b其中的一個去送 所以可以由多個狀態轉移過來
f[i+1][j] = max(f[i][j],f[i+1][j] + a[i+1]*a[i])//由i位置轉移過來
f[i][i+1] = max(f[i][i+1],f[i][j] + a[i+1]*a[j])//由j位置轉移過來
  • 但是這樣的動態轉移方程顯然很ex,畢竟一堆限制條件 再來仔細品 因為要保證前max(i,j)的位置都要送到 所以f[i][j]和f[j][i]是等效的 畢竟誰在前面都一樣
  • 所以我們的狀態轉移方程就可以表示為
f[i+1][j] = max(f[i][j],f[i+1][j] + a[i+1]*a[i])
f[i+1][i] = max(f[i][i+1],f[i][j] + a[i+1]*a[j])

程式碼展示

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2500+10;
int a[maxn],f[maxn][maxn];

int main(){
	int n,ans = 0;
	scanf("%d",&n);
	for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
	for(int i = 1;i <= n;++i)
		for(int j = 0;j < i;++j){
			f[i+1][j] = max(f[i][j] + a[i+1]*a[i],f[i+1][j]);
			f[i+1][i] = max(f[i][j] + a[i+1]*a[j],f[i+1][i]);
		}
	for(int i = 0;i < n;++i)ans = max(ans,f[n][i] + a[n]*a[i]);
	printf("%d",ans);
	return 0;
}