zoukankan      html  css  js  c++  java
  • 压缩函数(多维动规)

    【题目 描述】

    定义一个对 01 串的压缩函数 f: f(空串) = 空串。 f(s) = s。

    其中 s 是一个 01 串。

    f(s1, s2) = t, t 是最短的满足 s1 是 t 的前缀且 s2 是 t 的后缀的 01 串。

    如 f(001, 011) = 0011, f(111, 011) = 111011。 f(a1, a2, …, an) = f( f(a1, a2, …, a[n-1] ) , an)

    如 f(000, 000, 111) = f( f(000, 000) , 111) = f(000, 111) = 000111

    对于给定的 n 个长度相等的 01 串 a[1] , a[2] , …, a[n] , 将其分为两个子 序列 b[1] , b[2] , …, b[k] 和 c[1] , c[2] , … , c[n-k] , 使得 | f(b[1] , b[2] , … b[k] ) | + | f(c[1] , c[2] , … c[n-k] ) | 最小化。

    不允许改变 n 个串之间的相对顺序, 每个串属于且仅属于一个子序列, 允许 某个子序列为空。

    注: | s| 表示 01 串 s 的串长度。

    【输入格式】

    第一行一个整数 n, 表示 01 串的个数。 以下 n 行, 每行 m 个 0 或 1。 第 i+1 行表示 a[i] 。

    【输出格式】

    输出 一 个整数, 表 示| f(b[1] , b[2] , … b[k] ) | + | f(c[1] , c[2] , … c[n-k] ) | 的最小值。

    【样例输入 1】

    3

    01

    10

    01

    【样例输出 1】

    4

    【样例输入 2】

    4

    000

    111

    110

    001

    【样例输出 2】

    8

    【样例解释】

    样例一: 另一个子序列为空, 则| f(01, 10, 01) | = | 0101| = 4;

    样例二: b = {000, 001} , c = {111, 110} , | f(000, 001) | + | f(111, 110) | =| 0001| + | 1110| = 8;

    【数据范围与约定】

    对于 20%的数据, 1 <= n <= 10, 1 <= m <= 5;

    对于另外 20%的数据, m = 1;

    对于 60%的数据, 1 <= n <= 1000, 1 <= m <= 10

    对于 100%的数据, 1 <= n <= 2*10^5, 1 <= m <= 20;


     感觉这道DP题出的很好欸,有点锻炼思考能力的。考试的时候想到了一个和正解相差不多的DP方程,但是那个方法会超时,还是正解的比较棒啦。

    下面是官方题解原文

    对于 100%的数据, 1 <= n <= 2*10^5, 1 <= m <= 20;

    设当前处理位置的数字为 abcde,该数字接在某一子序列末尾,则仅需考虑子序列末尾的串的后缀是否是 空, a, ab, abc, abcd, abcde。

    需要考虑的情况总数: O(m)

    f[i][j][x] 表示前 i 个串, 第 i 串在一个子序列末尾, 另一个子序列末尾的后 j 位为 x 时的最优解。

    分两种情况讨论:

                  

       (1) 接在第 i-1 串所在子序列的后面, f[i][j][x] =f[i-1][j][x] +m-k;

       (2) 接在另一个子序列后面, 枚举第 i 串的前 j 位与该序列末尾重合,则最优值为 tmp=min(f[i-1] [j] [a[i] 的前 j 位] +m-j) , 此时需要记录的是第 i-1 串所在序列的末尾的状态, 用 tmp 的值去更新即可。

    可以发现(2) 操作的 f[i] 是在 f[i-1] 的基础上改变 m 个数,时间复杂度为 O(m),而(1) 操作的 f[i] 相当于是对整个 f[i-1] 加上一个数,省去第一维,记录一个全局变量打上标记即可。时间复杂度 O(n*m)

    下面是自己的话:

       总之,当无法理解DP方程时,一定要牢记初心(数组的含义),即,此时我盖掉某个串,另一个串的后缀状态是 j k (后面j位状态为k) 时的最优值。

       具体的全局变量更新一定要考虑清楚,加入新的值时,要记得在数组中存减掉SM(我自己定的全局变量)后的值,因为前面的那一部分SM并没有更新到此时的状态里,最后找答案时再加上去就可以了。

       哦,还有,不要忽略可以从空串的地方更新哦。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<algorithm>
     6 
     7 #define For(i,a,b) for(register int i=a;i<=b;++i)
     8 #define Re register
     9 #define inf 0x7f7f7f
    10 using namespace std;
    11 const int N=2e5+10,ST=(1<<20)+1;
    12 int f[21][ST],SM=0;
    13 int a[N],n,m,len;
    14 char c[30];
    15 inline void read(int &v){
    16     v=0;
    17     char c=getchar();
    18     while(c<'0'||c>'9')c=getchar();
    19     while(c>='0'&&c<='9')v=v*10+c-'0',c=getchar();
    20 }
    21 char cx[30],cy[30];
    22 int getTOG(int x,int y){
    23     int ans=m+1;
    24     For(i,1,m)if(x&(1<<(i-1)))cx[m-i+1]='1'; else cx[m-i+1]='0';
    25     For(i,1,m)if(y&(1<<(i-1)))cy[m-i+1]='1'; else cy[m-i+1]='0';
    26     
    27     For(p1,1,m){
    28         bool sc=1;
    29         For(i,p1,m){
    30             if(cy[i-p1+1]!=cx[i]){
    31                 sc=0; break;
    32             }
    33         }
    34         if(!sc)continue;
    35         else {ans=p1; break;}
    36     } 
    37     return ans-1;
    38 }
    39 
    40 int main(){
    41 //    freopen("compress.in","r",stdin);
    42 //    freopen("compress.out","w",stdout);
    43     read(n);
    44     For(i,1,n){ 
    45         scanf("%s",c+1);
    46         len=strlen(c+1);
    47         m=len;
    48         For(j,1,len)if(c[j]=='1'){
    49             a[i]= a[i] | (1<<(len-j)) ;
    50         }
    51     }
    52     memset(f,-inf,sizeof(f));
    53     int nf=f[0][0];
    54     int fst=(1<<m)-1;
    55     f[0][0]=m;
    56     SM=0;
    57 
    58     For(i,2,n){
    59         int adx=getTOG(a[i-1],a[i]);
    60         int tmp=-nf;
    61         For(j,0,m){
    62             int jst=a[i]>>(m-j);  //求前缀 
    63             if(f[j][jst]!=nf){
    64                 tmp=min(tmp,f[j][jst]+m-j+SM);  
    65             }
    66         }
    67      
    68         int px=fst,ast=a[i-1];
    69         
    70         SM+=adx; //更新全局变量 
    71         
    72         For(j,0,m){
    73             ast=(ast&px);    //求后缀 
    74             if(f[m-j][ast]==nf)f[m-j][ast]=tmp-SM;
    75             else f[m-j][ast]=min(f[m-j][ast],tmp-SM);
    76             px>>=1;
    77         }
    78     }
    79     
    80     int fn=-nf;
    81     For(i,0,m) For(j,0,fst)if(f[i][j]>nf){
    82         fn=min(fn,f[i][j]+SM);
    83     }
    84     cout<<fn<<endl;     
    85     fclose(stdin); fclose(stdout);
    86     return 0;
    87 }

     

     

  • 相关阅读:
    java泛型
    跨域传递
    laravel的一些语法
    去重
    laravel的一些查询语句
    mysql把之前表单进行拆分
    Laravel5.1接收json数据
    thinkphp5 composer安装验证码
    关于地图经纬度的问题
    tp5分组查询
  • 原文地址:https://www.cnblogs.com/HLAUV/p/9877229.html
Copyright © 2011-2022 走看看