• 【HDU4960】Another OCD Patient


    题意

       给出一个长度为n的整数序列。可以将一段连续的序列进行合并。合并的长度不同代价不同。问付出最少多少代价可以将这个序列变成一个对称的序列。n<=5000

    分析

       一看题感觉是个dp很好写啊。f[i][j]为令区间[i,j]对称的最小花费。那么转移并不难想

       f[i][j]=min(f[i][j],f[i+l1][j-l2]+val[l1-i+1]+val[j-l2+1]  |  sum[l1]-sum[i-1]==sum[j]-sum[l2-1]);

       然后按照区间dp的写法就写了一个迭代然后T掉了。看了一下这样好像是n^3的?下面是T的代码

      

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <vector>
     5 using namespace std;
     6 typedef long long LL;
     7 const int inf = 0x3f3f3f3f;
     8 const int MAX = 5000+10;
     9 int dp[MAX][MAX],a[MAX],v[MAX];
    10 LL sum[MAX];
    11 int dfs(int L,int R) {
    12     if(L>=R) return 0;
    13     if(~dp[L][R]) return dp[L][R];
    14     LL sum1,sum2; int ans=a[R-L+1];
    15     for(int i=L,j=R;i<j;) {
    16         sum1=sum[i]-sum[L-1];
    17         sum2=sum[R]-sum[j-1];
    18         if(sum1==sum2) {
    19             ans=min(ans,dfs(i+1,j-1)+a[i-L+1]+a[R-j+1]);
    20             i++; j--;
    21         }
    22         else if(sum1>sum2) j--;
    23         else i++;
    24     }
    25     return dp[L][R]=ans;
    26 }
    27 int main() {
    28     int n;
    29     while(scanf("%d",&n)==1&&n) {
    30         memset(sum,0,sizeof(sum));
    31         for(int i=1;i<=n;i++) {
    32             scanf("%d",&v[i]);
    33             sum[i]=sum[i-1]+v[i];
    34         }
    35         for(int i=1;i<=n;i++) {
    36             scanf("%d",&a[i]);
    37         }
    38         memset(dp,-1,sizeof(dp));
    39         int ans=dfs(1,n);
    40         printf("%d
    ",ans);
    41     }
    View Code

      然后我去网上查了一下,发现这个思想用记忆搜索可以过。。。emmm想一下,貌似是啊,记忆搜索是n^2logn?下面是记忆搜索的代码

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <iostream>
     5 
     6 using namespace std;
     7 const int maxn=5000+100;
     8 const int INF=2147000000;
     9 int a[maxn],val[maxn],f[maxn][maxn];
    10 int n;
    11 long long sum[maxn];
    12 int dfs(int L,int R){
    13     if(f[L][R]!=-1)
    14         return f[L][R];
    15     f[L][R]=val[R-L+1];
    16     for(int i=L,j=R;i<j;){
    17         long long sum1=sum[i]-sum[L-1];
    18         long long sum2=sum[R]-sum[j-1];
    19         if(sum1==sum2){
    20             f[L][R]=min(f[L][R],dfs(i+1,j-1)+val[i-L+1]+val[R-j+1]);
    21             i++,j--;
    22         }
    23         else if(sum1>sum2)j--;
    24         else i++;
    25     }
    26     return f[L][R];
    27 }
    28 int main(){
    29     while(scanf("%d",&n)!=EOF&&n){
    30         sum[0]=0;
    31         for(int i=1;i<=n;i++){
    32             scanf("%d",&a[i]);
    33             sum[i]=sum[i-1]+a[i];
    34         }
    35         for(int i=1;i<=n;i++)
    36             scanf("%d",&val[i]);
    37         memset(f,-1,sizeof(f));
    38         int ans=dfs(1,n);
    39         printf("%d
    ",ans);
    40     }
    41 return 0;
    42 }
    View Code
     

     然后又开始想迭代该怎么写···

     先按照上面的方法预处理出一个pos[maxn]数组,j=pos[i],sum[i]=sum[n]-sum[j-1]。也就是说1-i的和等于pos[i]到n的和。

     设一个dp数组f[i]为将1-i和将pos[i]到n合并的最小花费是多少。

     转移:f[i]=min(f[i],f[j]+val[i-j]+val[pos[j]-pos[i]])

     最终答案是 ans=min(f[i]+val[pos[i]-i-1]);

     恩,就是这样。下面是AC代码

     

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <algorithm>
     4 #include <iostream>
     5 
     6 using namespace std;
     7 const int INF=2147480000;
     8 const int maxn=5000+100;
     9 int a[maxn],val[maxn],f[maxn],pos[maxn];
    10 long long sum[maxn];
    11 
    12 int n;
    13 int main(){
    14     while(scanf("%d",&n)!=EOF&&n){
    15         sum[0]=0;
    16         memset(pos,0,sizeof(pos));
    17         for(int i=1;i<=n;i++){
    18             scanf("%d",&a[i]);
    19             sum[i]=sum[i-1]+a[i];
    20         }
    21         for(int i=1;i<=n;i++)
    22             scanf("%d",&val[i]);
    23         int i=1,j=n;
    24         while(i<j){
    25             long long sum1=sum[i];
    26             long long sum2=sum[n]-sum[j-1];
    27             if(sum1==sum2){
    28                 pos[i]=j;
    29                 i++,j--;
    30             }
    31             else if(sum1<sum2)i++;
    32             else j--;
    33         }
    34         memset(f,0,sizeof(f));
    35         /*for(int i=1;i<=n;i++){
    36             printf("%d ",pos[i]);
    37         }*/
    38         f[0]=val[n];
    39         for(int i=1;i<=n;i++){
    40                 if(!pos[i])continue;
    41                 f[i]=val[i]+val[n-pos[i]+1];
    42             for(int j=0;j<i;j++){
    43                 if(pos[j]!=0){
    44                     f[i]=min(f[i],f[j]+val[i-j]+val[pos[j]-pos[i]]);
    45                 }
    46             }
    47         }
    48       int ans=val[n];
    49 
    50         for(int i=0;i<=n;i++)
    51             if(pos[i]||i==0){
    52               if(pos[i]>i+1)
    53               ans=min(ans,f[i]+val[pos[i]-i-1]);
    54               else
    55               ans=min(ans,f[i]);
    56             }
    57         printf("%d
    ",ans);
    58 
    59       /*  for(int i=0;i<=n;i++)
    60             printf("%d %d %d
    ",i,pos[i],f[i]);*/
    61     }
    62 return 0;
    63 }
    View Code
  • 相关阅读:
    Scala篇:Scala环境及IDEA配置
    大数据篇:Hive
    大数据篇:Flume
    大数据篇:Kafka
    15.百万考生成绩如何排序
    Tomcat 架构原理解析到架构设计借鉴
    服务设计思考:平台化
    Druid:通过 Kafka 加载流数据
    12.分而治之-归并排序
    11.经典O(n²)比较型排序算法
  • 原文地址:https://www.cnblogs.com/LQLlulu/p/8986612.html
Copyright © 2020-2023  润新知