100道动态规划——40 CSUOJ 1889 Copying DNA 记忆化搜索 DP的感觉

来源:互联网 发布:大麦户源码安装教程 编辑:程序博客网 时间:2024/05/28 15:35

Description

Evolution is a seemingly random process which works in a way which resembles certain approaches we use to get approximate solutions to hard combinatorial problems. You are now to do something completely different.
Given a DNA string S from the alphabet {A,C,G,T}, find the minimal number of copy operations needed to create another string T. You may reverse the strings you copy, and copy both from S and the pieces of your partial T. You may put these pieces together at any time. You may only copy contiguous parts of your partial T, and all copied strings must be used in your final T. Example: From S = “ACTG” create T = “GTACTATTATA”

  1. Get GT......... by copying and reversing “TG” from S.
  2. Get GTAC....... by copying “AC” from S.
  3. Get GTAC...TA.. by copying “TA” from the partial T.
  4. Get GTAC...TAAT by copying and reversing “TA” from the partial T.
  5. Get GTACAATTAAT by copying “AAT” from the partial T.

Input

The first line of input gives a single integer, 1<=t<=100, the number of test cases. Then follow, for each test case, a line with the string S of length 1<=m<=18, and a line with the string T of length 1<=n<=18.

Output

Output for each test case the number of copy operations needed to create T from S, or “impossible” if it cannot be done.

Sample Input

5ACGTGTACACACGTTGCAACGTTCGATCGAAAAAAAAAAAAAAAAAAAA

Sample Output

2impossible146

Hint

Source

2007 Nordic Collegiate Programming Contest


我这里先把官方的题解给出来吧:

This problem can not be solved greedily or with dynamic programming. You must test every order of building the parts of the target string. However, you can get a big speed-up from memoising the optimal answers to partial solutions, as many branches in the bruteforce search will end up in the same state. Also, when trying to build a part of the target string starting at a given position, it will always pay off to build as large a portion of the string as possible.


手动翻译一下:

这道题不能用贪心或者动态规划来解决。你必须尝试能够成为目标串的一部分的串的所有顺序。但是你可以通过记录部分串的最优解来极大的提升速度。因为在暴力搜索的时候许多分支最终将会落在同样的状态上面。同时,当你选定了一个位置开始填充目标串时,填最长能够充填的串。


好了,表示英文并不好。勉强看看吧

这道题或许没有平常的动态规划的转移方程吧,但是我感觉其骨子里还是有动态规划的。毕竟动态规划的英文是dynamic programming,这里的programming指的是一种表格法,不是编程。也就是开一张表来存储答案,然后以较低的时间复杂度来查询最优解。这里用到了这个原始的概念。

其次就是选定一个充填位进行充填的时候,充填最长的串(这里的意思不是每一次充填当前能够充填最长的串),其最优性也是可以证明的。以前可以充填的串,在这一次操作之后,依然可以充填,而且这样充填完之后,又可以保证可能会有新的copy串出现,这样是有道理的。

比较好的一道题目。

定义二进制状态i,其每一位表示该位是否被填充。对于状态i来说,枚举还没有被填充的位k,根据刚刚说的,我们现在要从k位开始填充,找到最长能够填充的串,更新当前的最优解。最后的答案是solve(0)

这里放一份标程吧:

标程挺详细的

#include <algorithm>#include <cstdio>using namespace std;const int INF = 1 << 28;const int MAX_m = 20;const int MAX_n = 20;int memo[1 << MAX_n];char S[MAX_m+1];char Sr[MAX_m+1];char T[MAX_n+1];int m, n;/* * Find the longest match for T[i..i+g] in A of length e */int match(int i, int g, char* A, int e) {    int longest = 0;    for (int k = 0; k < e; k++) {        int h = 0;        while (h < g && k+h < e && A[k+h] == T[i+h]) h++;        longest = max(longest, h);    }    return longest;}/* * found is a bitmask giving which symbols have already been copied. */int solve(unsigned int found) {    if (memo[found] != -1) {        return memo[found];    }    // Let U be the string found so far,    // and Ur its reverse    char U[MAX_n+1];    char Ur[MAX_n+1];    fill(U,  U +n, 0);    fill(Ur, Ur+n, 0);    for (int i = 0; i < n; i++) {        if (found & 1 << i) U[i] = T[i];    }    reverse_copy(U, U+n, Ur);    int best = INF; // The number of copy operations needed    int last_h = 0;    for (int i = 0; i < n; i++) {        int g = 0; // The length of the missing piece starting at T[i..]        while (i + g < n && !(found & 1 << i+g)) g++;        if (g == 0) {            last_h = 0;            continue;        }        int h = 0; // The longest match for T[i..i+g]        h = max(h, match(i, g, S,  m)); // copied from S        h = max(h, match(i, g, Sr, m)); // reversed from S        h = max(h, match(i, g, U,  n)); // copying from T        h = max(h, match(i, g, Ur, n)); // reversed from T        if (h == 0) return INF; // Nothing matches T[i..] at all        if (h > last_h - 1) { // If not, we cannot improve            // Set bits from i+h downto i            int newfound = found | (1 << i+h)-1 ^ (1 << i)-1;            best = min(best, 1 + solve(newfound));        }         if (best == INF) break; // Nothing can be filled in here        last_h = h;    }    memo[found] = best;    return best;}int main() {    int t;    scanf("%d", &t);    for (int i = 0; i < t; i++) {        scanf("%s", S);        m = strlen(S);        reverse_copy(S, S+m, Sr);        scanf("%s", T);        n = strlen(T);         fill(memo, memo + (1 << MAX_n), -1);        memo[(1 << n) - 1] = 0;        int s = solve(0);        if (s == INF) printf("impossible\n");        else          printf("%d\n", s);    }}


然后是我自己按标程写的代码,900ms左右过的,悬。时限1S

顺便说一下,它那个set bits from i+h downto i 是一个位运算的小技巧了,可以尝试学习一下

#include <iostream>#include <cstring>#include <algorithm>#include <bitset>using namespace std;const int maxm=20;int memo[1<<maxm],n,m,times,match(int i,int g,char* str,int e),solve(int found);char s[maxm],sr[maxm],t[maxm];int main(){    ios_base::sync_with_stdio(0);    cin>>times;    while(times--){        cin>>s>>t;        m=strlen(s),n=strlen(t);        reverse_copy(s,s+m,sr);        memset(memo,-1,sizeof(int)*(1<<n));        memo[(1<<n)-1]=0;        if(solve(0)==0x3f3f3f3f)            cout<<"impossible\n";        else            cout<<solve(0)<<endl;    }    return 0;}int match(int i, int g, char* str, int e){    int longest=0;    for(int k=0,h=0;k<e;++k,h=0){        while(h<g&&k+h<e&&str[k+h]==t[i+h])++h;        longest=max(h,longest);    }    return longest;}int solve(int found){    if(memo[found]!=-1)        return memo[found];    char u[maxm]{},ur[maxm]{};    int best=0x3f3f3f3f,last_h=0;    for(int i=0;i<n;++i)    if(found&(1<<i))u[i]=t[i];    reverse_copy(u,u+n,ur);    for(int i=0,g=0,h=0;i<n;++i,g=h=0){        while(i+g<n&&!(found&(1<<i+g)))++g;        if(g==0)            last_h=0;        else{            h=max(max(match(i,g,sr,m),match(i,g,s,m)),max(match(i,g,u,n),match(i,g,ur,n)));            if(h==0)return 0x3f3f3f3f;            if(h>last_h-1)                best=min(best,1+solve(found|((1<<i+h)-1)^((1<<i)-1)));            if(best==0x3f3f3f3f)                break;            last_h=h;        }    }    return memo[found]=best;}





0 0