[JZOJ3105]拼图

题目大意:
   给你一个起始串$a(|a|\leq 300)$,一个目标串$b(|b|\leq 300)$,以及$n(n\leq 8)$个小串$s_0,s_2,\ldots,s_{n-1}(|s_i|\leq 400)$,你可以进行若干次操作将$a$变成$b$。
  操作的规则如下:
​  1.取出每个小串的任一后缀,代价为去除后缀的长度;
  2.移除起始串中的若干字符,每移除一个字符需要$1$的代价。
  3.选择一些小串并将其插入到$a$中(包括左边界,不包括右边界);
  问将$a$变成$b$的最小代价。

思路:
  状压DP。
  用$f_{i,j}=k$表示选取小串的状态为$i$,目标串匹配长度为$j$时,起始串$a$匹配的最少长度为$k$。
  对于状态$f_{i,j}$转移时分两种情况,一种是用起始串的一个字母匹配,另一种是用小串来匹配。
  用起始串匹配时,找到$a$中下一个和$b_{j+1}$相同的字符$a_k$,向$f_{i,j+1}$转移。
  用小串匹配时,枚举没有选过的小串$s_k$找到能和目标串匹配的最长前缀长度$l$,向$f_{i+2^k,j+l}$转移。
  最后统计答案时,枚举最后小串选取的状态,答案即为起始串长度+选取的小串长度-目标串长度。
  边界情况:$f_{i,j}\geq|a|$,这时再转移就到了$a$的右边界,因此不予转移。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<cstring>
 4 #include<algorithm>
 5 inline int getint() {
 6     register char ch;
 7     while(!isdigit(ch=getchar()));
 8     register int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const unsigned inf=~0u;
13 const int LEN1=302,LEN2=402,N=8;
14 char s1[LEN1],s2[LEN1],s[N][LEN2];
15 unsigned f[1<<N][LEN1],len1,len2,len3[N];
16 int main() {
17     scanf("%s%s",&s1[1],&s2[1]);
18     len1=strlen(&s1[1]),len2=strlen(&s2[1]);
19     const int n=getint();
20     for(register int i=0;i<n;i++) {
21         scanf("%s",&s[i][1]);
22         len3[i]=strlen(&s[i][1]);
23     }
24     memset(f,0xff,sizeof f);
25     f[0][0]=0;
26     for(register int i=0;i<1<<n;i++) {
27         for(register int j=0;j<(int)len2;j++) {
28             if(f[i][j]>=len1) continue;
29             for(register unsigned k=f[i][j]+1;s1[k];k++) {
30                 if(s1[k]==s2[j+1]) {
31                     f[i][j+1]=std::min(f[i][j+1],k);
32                     break;
33                 }
34             }
35             for(register int k=0;k<n;k++) {
36                 if(i&(1<<k)) continue;
37                 for(register int l=1;s[k][l]&&j+l<=(int)len2&&s[k][l]==s2[j+l];l++) {
38                     f[i|(1<<k)][j+l]=std::min(f[i|(1<<k)][j+l],f[i][j]);
39                 }
40             }
41         }
42     }
43     unsigned ans=inf;
44     for(register int i=0;i<1<<n;i++) {
45         if(f[i][len2]==inf) continue;
46         unsigned tmp=0;
47         for(register int j=0;j<n;j++) {
48             if(i&(1<<j)) tmp+=len3[j];
49         }
50         ans=std::min(ans,len1+tmp-len2);
51     }
52     printf("%u\n",ans);
53     return 0;
54 }

 

posted @ 2018-01-08 11:03  skylee03  阅读(125)  评论(0编辑  收藏  举报