题目链接:https://atcoder.jp/contests/agc062/tasks/agc062_b
做法
非常有意思的一道题目。
我们考虑一个序列被切割以后进入一种如放的状态,简单来说就是我手里现在有两个序列可以拼起来。
当操作一次后会发现手里的两个序列变成了四个序列,而代价实际上就是原来两个序列裂成两个序列的代价之和(因为代价的计算公式是一次式)。
因此,显然,我们要把原串切成一堆上升序列,而代价都是可以自己独立统计后再加起来的,直接上区间 DP ,时间复杂度:$O(Kn^3)$ 。
无解的充分必要条件:最少的上升序列个数 $>2^K$ 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
   | #include<cstdio> #include<cstring> #include<algorithm> #define N 110 using namespace std; typedef long long LL; LL dp[N][N][N],C[N]; int n,K,a[N]; int main(){     scanf("%d%d",&n,&K);     for(int i=1;i<=K;i++)scanf("%lld",&C[i]);     for(int i=1;i<=n;i++){         int x;scanf("%d",&x);         a[x]=i;     }               memset(dp,20,sizeof(dp));     int cnt=0;     for(int i=1;i<n;i++){         if(a[i]>a[i+1])cnt++;     }     for(int i=1;i<=K;i++)cnt>>=1;     if(cnt){printf("-1\n");return 0;}     for(int i=1;i<=n;i++){         dp[K+1][i][i]=0;         for(int j=i+1;j<=n;j++){             if(a[j]<a[j-1])break;             dp[K+1][i][j]=0;         }     }     for(int t=K;t>=1;t--){         for(int i=1;i<=n;i++){             for(int j=i;j<=n;j++){                 dp[t][i][j]=dp[t+1][i][j];                 for(int k=i;k<j;k++)dp[t][i][j]=min(dp[t+1][i][k]+dp[t+1][k+1][j]+(j-k)*C[t],dp[t][i][j]);             }         }     }                              printf("%lld\n",dp[1][1][n]);     return 0; }
   |