ACM-ICPC 2018 焦作賽區網路預賽 Mathematical Curse (簡單DP+維護極值)
阿新 • • 發佈:2018-12-10
#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; }