ZOJ3545——AC自动机+状态dp

这题目是去年大连regional的题目,当时比赛的时候已经看出是道自动机+dp的题目了,但是无奈当时自己的自动机基础太弱了,被虐了一顿。今天再来做这道题目,感觉还好,思路还是蛮清晰的,但是却看出了长时间写代码少代码能力的不足。。。一个<=写成了<然后昨天WA了一晚上。。。

这题和传统的自动机+dp差不多,主要区别在于每个带权基因串只计算一次,需要用状态压缩(字符串个数最大只有10个)。dp[l][n][state]表示长度为l的字符串,后缀为自动机中第n类状态(自动机中每个节点代表一类状态),是否可取道带权基因串组合状态为state的情况;如果可以dp[l][n][state]为true,否则为false。计算的时候和大部分自动机dp一样,暴力递推求解就可以了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <string>
#include <utility>
#include <algorithm>
using namespace std;

const int N = 10;

struct node {
    int num, index;
    node *pre, *next[4];
    void init() {
        num = index = 0;
        pre = 0;
        memset(next, 0, sizeof(next));
    }
};

int n, l, cnt, val[10], sval[1<<N];
bool dp[2][N*150][1<<N];
char str[150];

map <char, int> h;
node trie[N*150];
node *root, *queue[N*150];

void insertStr(node *root, char *str, int pos) {
    int lab, len = strlen(str);
    for(int i = 0; i < len; i++) {
        lab = h[str[i]];
        if(root->next[lab] == NULL) {
            root->next[lab] = &trie[++cnt];
            root->next[lab]->init();
            root->next[lab]->index = cnt;
        }
        root = root->next[lab];
    }
    root->num |= (1<<pos);
}

void buildAC() {
    int head, tail;
    node *u;
    root->pre = root;
    head = tail = 0;
    queue[0] = root;
    while(head <= tail) {
        u = queue[head++];
        u->num |= u->pre->num;
        for(int i = 0; i < 4; i++) {
            if(u->next[i] == 0) {
                if(u == root)  u->next[i] = root;
                else  u->next[i] = u->pre->next[i];
            }
            else {
                if(u == root)  u->next[i]->pre = root;
                else  u->next[i]->pre = u->pre->next[i];
                queue[++tail] = u->next[i];
            }
        }
    }
}

int main() {
    //freopen("data.in", "r", stdin);
    h['A'] = 0; h['G'] = 1; h['T'] = 2; h['C'] = 3;
    while(scanf("%d%d", &n, &l) != EOF) {
        memset(trie, 0, sizeof(trie));
        root = &trie[0];
        root->init();
        cnt = 0;
        for(int i = 0; i < n; i++) {
            scanf(" %s%d", str, &val[i]);
            insertStr(root, str, i);
        }
        memset(sval, 0, sizeof(sval));
        for(int state = 0; state < (1<<n); state++) {
            for(int j = 0; j < n; j++)
                if(state&(1<<j))
                    sval[state] += val[j];
        }
        buildAC();
        memset(dp, false, sizeof(dp));
        int u, v;
        u = 0;
        dp[u][0][0] = true;
        for(int i = 0; i < l; i++) {
            v = u^1;
            memset(dp[v], false, sizeof(dp[v]));
            for(int j = 0; j <= cnt; j++)
                for(int k = 0; k < (1<<n); k++) {
                    if(!dp[u][j][k])  continue;
                    else {
                        for(int p = 0; p < 4; p++) {
                            int dindex = trie[j].next[p]->index;
                            dp[v][dindex][k|trie[dindex].num] = true;
                        }
                    }
                }
            u = v;
        }
        int ans = -1;
        for(int i = 0; i <= cnt; i++)
            for(int j = 0; j < (1<<n); j++) {
                if(dp[u][i][j] == false)  continue;
                ans = max(ans, sval[j]);
            }
        if(ans != -1)  printf("%d\n", ans);
        else  printf("No Rabbit after 2012!\n");
    }
    return  0;
}

 

 

转载于:https://www.cnblogs.com/fCarver7/archive/2012/09/20/2696036.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值