【题目 描述】
定义一个对 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 }