題解 [IOI1998]Polygon
題目描述
多邊形是一個玩家在一個有n個頂點的多邊形上的遊戲,如圖所示,其中n=4。每個頂點用整數標記,每個邊用符號+(加)或符號*(乘積)標記。
第一步,刪除其中一條邊。隨後每一步:
選擇一條邊連線的兩個頂點V1和V2,用邊上的運算子計算V1和V2得到的結果來替換這兩個頂點。
遊戲結束時,只有一個頂點,沒有多餘的邊。
如圖所示,玩家先移除編號為3的邊。之後,玩家選擇計算編號為1的邊,然後計算編號為4的邊,最後,計算編號為2的邊。結果是0。

(翻譯者友情提示:這裡每條邊的運算子旁邊的數字為邊的編號,不拿來計算)
編寫一個程式,給定一個多邊形,計算最高可能的分數。
輸入格式
輸入描述一個有n個頂點的多邊形,它包含兩行。第一行是數字n,為總邊數。
第二行描述這個多邊形,一共有2n個讀入,每兩個讀入中第一個是字元,第二個是數字。
第一個字元為第一條邊的計算符號(t代表相加,x代表相乘),第二個代表頂點上的數字。首尾相連。
3 < = n < = 50
對於任何一系列的操作,頂點數字都在[-32768,32767]的範圍內。
輸出格式
第一行,輸出最高的分數。在第二行,它必須寫出所有可能的被清除後的邊仍能得到最高得分的列表,必須嚴格遞增。
Solution
這道題其實是一道典型的區間dp。
區間dp的一般做法是 列舉區間長度,然後對任意長度為列舉值區間進行dp的轉移。
在本題中,我們定義 $f[i][j]$表示當前的區間為$[i,j]$在運算後的最大值。$g[i][j]$表示當前區間為$[i,j]$在運算後的最小值。
由於題目中要求我們要刪去一條邊,我們很自然的能夠想到破環為鏈。直接把區間變為兩倍,取其中連續的n個數進行運算。
對於每一個區間,我們對其中的每一條邊進行列舉,把當前區間分為兩個區間。且這兩個區間由一個運算子連線。
那麼在轉移中就有多種情況,需要對乘法和加法進行分別考慮。
轉移方程如下:
乘法:
$f[l][r]=max(f[l][r],f[l][k]*f[k+1][r]);$
$f[l][r]=max(f[l][r],f[l][k]*g[k+1][r]);$
$f[l][r]=max(f[l][r],g[l][k]*f[k+1][r]);$
$f[l][r]=max(f[l][r],g[l][k]*g[k+1][r]);$
$g[l][r]=min(g[l][r],f[l][k]*f[k+1][r]);$
$g[l][r]=min(g[l][r],f[l][k]*g[k+1][r]);$
$g[l][r]=min(g[l][r],g[l][k]*f[k+1][r]);$
$g[l][r]=min(g[l][r],g[l][k]*g[k+1][r]);$
加法的轉移與上面基本一樣,這裡就不寫了。
最後只要判斷和統計一下答案就做完了
code:
\#include<bits/stdc++.h> #define INF 100000007 using namespace std; int n,a[105]; char s[105][2]; int f[105][105]; int g[105][105]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",s[i]); scanf("%d",&a[i]); } for (int i=1;i<=n;i++) { s[i+n][0]=s[i][0]; a[i+n]=a[i]; } for (int i=1;i<=n*2;i++) { for (int j=i;j<=n*2;j++) { if (i==j) { f[i][j]=a[i]; g[i][j]=a[i]; }else { f[i][j]=-INF; g[i][j]=INF; } } } for (int i=1;i<=n;i++) for (int j=1;j<=2*n-i+1;j++) { int l=j; int r=i+j-1; for (int k=l;k<=r-1;k++) { if (s[k+1][0]=='x') { f[l][r]=max(f[l][r],f[l][k]*f[k+1][r]); f[l][r]=max(f[l][r],f[l][k]*g[k+1][r]); f[l][r]=max(f[l][r],g[l][k]*f[k+1][r]); f[l][r]=max(f[l][r],g[l][k]*g[k+1][r]); g[l][r]=min(g[l][r],f[l][k]*f[k+1][r]); g[l][r]=min(g[l][r],f[l][k]*g[k+1][r]); g[l][r]=min(g[l][r],g[l][k]*f[k+1][r]); g[l][r]=min(g[l][r],g[l][k]*g[k+1][r]); } else { f[l][r]=max(f[l][r],f[l][k]+f[k+1][r]); f[l][r]=max(f[l][r],f[l][k]+g[k+1][r]); f[l][r]=max(f[l][r],g[l][k]+f[k+1][r]); f[l][r]=max(f[l][r],g[l][k]+g[k+1][r]); g[l][r]=min(g[l][r],f[l][k]+f[k+1][r]); g[l][r]=min(g[l][r],f[l][k]+g[k+1][r]); g[l][r]=min(g[l][r],g[l][k]+f[k+1][r]); g[l][r]=min(g[l][r],g[l][k]+g[k+1][r]); } } } int maxn=-INF; for (int i=1;i<=n+1;i++) { int l=i;int r=l+n-1; maxn=max(maxn,f[l][r]); } printf("%d\n",maxn); for (int i=1;i<=n;i++) { int l=i; int r=l+n-1; if (maxn==f[l][r]) printf("%d ",i); } }