【IOI1998】Polygon

一道区间dp题,不少细节。

一开始我想定义f[i][j]表示区间[i,j]通过某种合并之后的最大值,之后这道题就等同于“NOI1995石子合并”。

但是转念一想,这道题有加法和乘法两种运算,如果单看加法,以上思路正确。但是因为乘法的存在,两个较小的数(负数)相乘或许会大于两个最大数相乘,因此以上思路在这里出现了错误。

因此,我们定义maxx[i][j],minn[i][j]分别表示区间[i,j]通过某种合并之后的最大值、最小值。

1、如果当前枚举决策为k,而且运算符为加号,则有

 maxx[i][j]=max(maxx[i][k-1]+maxx[k][j]);

 minn[i][j]=min(minn[i][k-1]+minn[k][j]);

 如果运算符为乘号,则有

 maxx[i][j]=max(maxx[i][k-1]*maxx[k][j]);
 maxx[i][j]=max(minn[i][k-1]*minn[k][j]);
 minn[i][j]=min(maxx[i][k-1]*maxx[k][j]);
 minn[i][j]=min(minn[i][k-1]*minn[k][j]);
 minn[i][j]=min(maxx[i][k-1]*minn[k][j]);
 minn[i][j]=min(minn[i][k-1]*maxx[k][j]);

因此正确的算法就得出来了,我们枚举起始的位置开始计算即可,时间复杂度为O(n4).

在这里我们将这个环拆开并复制一倍,然后在这个两倍长的链上做一次dp,最后枚举起点选出答案即可,时间复杂度为O(n3).

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 int n,maxx[60<<1][60<<1],minn[60<<1][60<<1],a[60<<1];
 6 char op[60<<1];
 7 int main() {
 8     cin>>n;
 9     memset(minn,0x3f,sizeof(minn));
10     memset(maxx,-0x3f,sizeof(maxx));
11     for(int i=1;i<=n;i++) {
12         char x;
13         int y;
14         cin>>x>>y;
15         a[i]=a[i+n]=y;
16         op[i]=op[i+n]=x;
17     }
18     for(int i=1;i<=n<<1;i++) 
19         maxx[i][i]=minn[i][i]=a[i];
20     for(int i=2;i<=n;i++) {
21         for(int l=1;l+i-1<=2*n;l++) {
22             for(int k=l+1;k<=l+i-1;k++) {
23                 if(op[k]=='t') {
24                     maxx[l][l+i-1]=max(maxx[l][l+i-1],maxx[l][k-1]+maxx[k][l+i-1]);
25                     minn[l][l+i-1]=min(minn[l][l+i-1],minn[l][k-1]+minn[k][l+i-1]);
26                 }
27                 else {
28                     maxx[l][l+i-1]=max(maxx[l][l+i-1],maxx[l][k-1]*maxx[k][l+i-1]);
29                     maxx[l][l+i-1]=max(maxx[l][l+i-1],minn[l][k-1]*minn[k][l+i-1]);
30                     minn[l][l+i-1]=min(minn[l][l+i-1],maxx[l][k-1]*maxx[k][l+i-1]);
31                     minn[l][l+i-1]=min(minn[l][l+i-1],minn[l][k-1]*minn[k][l+i-1]);
32                     minn[l][l+i-1]=min(minn[l][l+i-1],maxx[l][k-1]*minn[k][l+i-1]);
33                     minn[l][l+i-1]=min(minn[l][l+i-1],minn[l][k-1]*maxx[k][l+i-1]);
34                 }
35             }
36         }
37     }
38     int ans=-0x7fffffff;
39     for(int i=1;i<=n;i++)
40         ans=max(ans,maxx[i][i+n-1]);
41     cout<<ans<<endl;
42     for(int i=1;i<=n;i++)
43         if(ans==maxx[i][i+n-1]) cout<<i<<" ";
44     return 0; 
45 }
AC Code
原文地址:https://www.cnblogs.com/shl-blog/p/10664527.html