hdu3247 Resource Archiver

6 篇文章 0 订阅
5 篇文章 0 订阅

题面在这里

题意:

给你n个资源串,m个病毒串,都由01组成。需要构造一个新串使得n个资源串都是这个新串的子串,并且该新串不包含任意一个病毒串。求新串的最小长度。
2 <= n <= 10, 1 <= m <= 1000
每个资源串长度<=1000,所有病毒串长度<=50000。

做法:

对于所有的病毒串和资源串,把它们都扔进AC自动机处理。
对于每一个节点处理出是否含病毒,以及包含了哪几个资源串(状压)。
f[i][j]表示当前使用的资源串状压为i,现在在j这个节点的最小长度
如果直接枚举下一步走的节点转移,发现会超时,时间复杂度上界大概是(50000+10*1000)*1024*2+大常数>1s。

考虑优化,发现每次转移的时候,有很多节点不改变i这个状压,但我们最后需要的状压是2^n-1。所以一个一个节点转移十分浪费时间。
所以我们可以采用预处理的方式。ac自动机本身就是一个图,我们将所有资源串末尾节点取出来,每个点跑一个bfs,求出它到其他点的最短路(bfs的时候病毒串末尾不能走)。
于是每次只要转移到这几个资源串末尾节点就可以了,f[i][j]表示当前使用的资源串状压为i,现在在j这个节点(j是资源串末尾节点的编号)。

代码:

/*************************************************************
    Problem: hdu 3247 Resource Archiver
    User: fengyuan07
    Language: C++
    Result: Accepted
    Time: 62MS
    Memory: 3868K
    Submit_Time: 2018-01-23 20:40:22
*************************************************************/

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cctype>
#include<cstdlib>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;

const int N = 100010;
const int inf = 1e9;
int n, m, tot, cnt;
int c[N][2], fail[N], w[N], f[1500][60], e[60][60], pos[60], dis[N];
char str[N];

inline void insert(char s[], int x) {
    int len = strlen(s), o = 0;
    for(int i = 0; i < len; i ++) {
        int t = s[i]-'0';
        if(!c[o][t]) c[o][t] = ++ tot;
        o = c[o][t];
    }
    if(w[o] == -1) return;
    if(x == -1) w[o] = -1; else w[o] |= 1<<x-1;//-1是病毒串,否则记录当前含有资源串的状压
}
inline void build() {
    queue<int> q;
    for(int i = 0; i < 2; i ++) if(c[0][i]) q.push(c[0][i]), fail[c[0][i]] = 0;
    while(!q.empty()) {
        int u = q.front(); q.pop();
        if(w[fail[u]] == -1) w[u] = -1; else w[u] |= w[fail[u]];//把fail[u]节点的信息传到u上来
        for(int i = 0; i < 2; i ++)
            if(c[u][i]) q.push(c[u][i]), fail[c[u][i]] = c[fail[u]][i];
            else c[u][i] = c[fail[u]][i];
    }
}
inline void bfs(int s) {
    queue<int> q; q.push(pos[s]); for(int i = 0; i <= tot; i ++) dis[i] = inf;
    dis[pos[s]] = 0;
    while(!q.empty()) {
        int u = q.front(); q.pop();
        for(int i = 0; i < 2; i ++) {
            int v = c[u][i];
            if(v <= tot && w[v] >= 0)//是存在的节点,并且不是病毒串末尾
                if(dis[v] == inf) { q.push(v); dis[v] = dis[u]+1; }
        }
    } for(int i = 1; i <= cnt; i ++) if(dis[pos[i]] < inf) e[s][i] = dis[pos[i]];
}
int main() {
    while(~scanf("%d%d", &n, &m) && (n||m)) {
        tot = 0; memset(c, 0, sizeof c); memset(fail, 0, sizeof fail); memset(w, 0, sizeof w);
        for(int i = 1; i <= n; i ++) { scanf("%s", str); insert(str, i); }
        for(int i = 1; i <= m; i ++) { scanf("%s", str); insert(str, -1); }
        build();
        cnt = 1;//以下是一些预处理
        pos[1] = 0;
        for(int i = 0; i <= tot; i ++)
            if(w[i] > 0) pos[++ cnt] = i;//记录非病毒串且是资源串的末尾的节点
        memset(e, -1, sizeof e);//-1表示这两个点之间不存在路径
        for(int i = 1; i <= cnt; i ++) bfs(i);//对每个节点bfs找最短路
        //以下是dp,f[i][j]表示当前使用的资源串状压为i,现在在j这个节点(资源串末尾节点的编号)
        memset(f, 0x3f, sizeof f); f[0][1] = 0;
        for(int i = 0; i < 1<<n; i ++)
            for(int j = 1; j <= cnt; j ++) if(f[i][j] < 1e9) {
                for(int k = 1; k <= cnt; k ++) {
                    if(e[j][k] < 0 || j == k) continue;//两点之间不存在路径就退出
                    f[i|w[pos[k]]][k] = min(f[i|w[pos[k]]][k], f[i][j]+e[j][k]);
                }
            } int ans = inf;
        for(int i = 1; i <= cnt; i ++) ans = min(ans, f[(1<<n)-1][i]);
        printf("%d\n", ans);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值