1. 程式人生 > >經典問題五.【乘號兩邊有正負 區間dp】Polygon poj 1179

經典問題五.【乘號兩邊有正負 區間dp】Polygon poj 1179

題目描述:
這裡寫圖片描述
思路:
因為n並不大,所以很容易想到列舉一下第一步去掉的邊。
然後展開應為符號和點交雜的一條線,如果符號只有‘+’,那麼用區間dp求其最優解應該很簡單啦,同樣小區間推大區間,dp[i][j] = dp[i][k-1]+dp[k][j].現在考慮‘’,不能向‘+’一樣簡單的相乘就可以,因為如果兩邊是負數,那麼兩邊最小才能使值最大,那麼我們在過程最小值和最大值都需要記憶化,在‘’時,列舉四種:(minn*minn , maxn*minn , maxn*maxn , minn*maxn)取最優即可。

#include <cstdio>
#include <cstring>
#include <algorithm> using namespace std; const int INF = 0x3f3f3f3f; const int N = 110; int minn[N][N],maxn[N][N]; int record[N]; char ver[N]; int a[N]; int main() { int n; scanf("%d",&n); for(int i = 1; i <= n; i++) { getchar(); scanf("%c %d",&ver[i],&a[i]); ver[i+n] = ver[i]; a[i+n] = a[i]; } int
ans = -INF; for(int v = 1; v <= n; v++) { int x = v, y = v+n-1; memset(minn,0,sizeof(minn)); memset(maxn,0,sizeof(maxn)); for(int i = x; i <= y; i++) minn[i][i] = maxn[i][i] = a[i]; for(int len = 1; len < n; len++) { for
(int i = x; i+len <= y; i++) { int l = i, r = i+len; maxn[l][r] = -INF; minn[l][r] = INF; for(int k = l+1; k <= r; k++) { if(ver[k] == 't') { maxn[l][r] = max(maxn[l][r],maxn[l][k-1]+maxn[k][r]); minn[l][r] = min(minn[l][r],minn[l][k-1]+minn[k][r]); } else { maxn[l][r] = max(maxn[l][r],maxn[l][k-1]*maxn[k][r]); minn[l][r] = min(minn[l][r],maxn[l][k-1]*maxn[k][r]); maxn[l][r] = max(maxn[l][r],maxn[l][k-1]*minn[k][r]); minn[l][r] = min(minn[l][r],maxn[l][k-1]*minn[k][r]); maxn[l][r] = max(maxn[l][r],minn[l][k-1]*minn[k][r]); minn[l][r] = min(minn[l][r],minn[l][k-1]*minn[k][r]); maxn[l][r] = max(maxn[l][r],minn[l][k-1]*maxn[k][r]); minn[l][r] = min(minn[l][r],minn[l][k-1]*maxn[k][r]); } } } } //printf("%d\n",maxn[x][y]); record[v] = maxn[x][y]; if(maxn[x][y] > ans) ans = maxn[x][y]; } printf("%d\n",ans); int flag = 0; for(int v = 1; v <= n; v++) { if(record[v] == ans) { if(flag == 0) printf("%d",v); else printf(" %d",v); flag = 1; } } puts(""); return 0; }