經典問題五.【乘號兩邊有正負 區間dp】Polygon poj 1179
阿新 • • 發佈:2019-02-06
題目描述:
思路:
因為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;
}