压缩函数(多维动规)

【题目 描述】

定义一个对 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 }

 

 

 

 

 

转载于:https://www.cnblogs.com/HLAUV/p/9877229.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值