1. 程式人生 > >ACM-ICPC 2018 焦作賽區網路預賽 Mathematical Curse (簡單DP+維護極值)

ACM-ICPC 2018 焦作賽區網路預賽 Mathematical Curse (簡單DP+維護極值)

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int  maxn =1e3+5;
const int mod=1e9+7;
ll dat[maxn];
char op[10];
/*
題目大意:題目沒怎麼仔細看。。。
看樣例我大概能感覺到,
m種符號必須選,然後n個數字可以有選擇性的選,
並且要按序,求最優值。

一個較為經典的DP問題,
如果要求出最大解,維護最大值是不能得到的,
因為有負數的參與,所以我們維護最大值和最小值。
首先分析區間性質,假設到了i,j二維點,
i,j二維點的最大值最小值如何產生呢,
不管那麼多,反正答案肯定是由極值產生的,
所以我們dp陣列維護的就是極值,
所以用維護好的兩個極值去產生運算,最後在二維點取最大最小即可維護起來。
我有個WA點,,,,選擇區間記得還有不選的選項,我WA了四次。。。就因為那一行。。。

PS:不想初始化DP陣列的朋友要手動精確的推答案產生的區間,就是定義域啦。T_T
*/

ll dp1[10][maxn],dp2[10][maxn];///維護一個最大值的DP,一個最小值的DP
ll cpt(ll x,ll y,char p)
{
    if(p=='+') return 1LL*(x+y);
    if(p=='-') return 1LL*(x-y);
    if(p=='*') return 1LL*(x*y);
    if(p=='/') return 1LL*(x/y);
}
int n,m,q;
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d",&n,&m,&q);
        for(int i=0;i<n;i++) scanf("%lld",&dat[i]);
        scanf("%s",op);
        dp1[0][0]=dp2[0][0]=cpt(q,dat[0],op[0]);
        for(int i=1;i<n-m+1;i++)
        {
            dp1[0][i]=max(dp1[0][i-1],cpt(q,dat[i],op[0]));
            dp2[0][i]=min(dp2[0][i-1], cpt(q,dat[i],op[0]));
        }
        for(int i=1;i<m;i++)
        {
            dp1[i][i]=cpt(dp1[i-1][i-1],dat[i],op[i]);
            dp2[i][i]=cpt(dp2[i-1][i-1],dat[i],op[i]);
            for(int j=i+1;j<n-m+i+1;j++)
            {
                dp1[i][j]=dp1[i][j-1],dp2[i][j]=dp2[i][j-1];///這一行必須有,維護一個不做選擇的解
                ll tp1=cpt(dp1[i-1][j-1],dat[j],op[i]);
                ll tp2=cpt(dp2[i-1][j-1],dat[j],op[i]);
                dp1[i][j]=max(dp1[i][j],max(tp1,tp2));
                dp2[i][j]=min(dp2[i][j],min(tp1,tp2));
             }
         }
        printf("%lld\n",dp1[m-1][n-1]);
    }
    return 0;
}