hdu 3247 Resource Archiver
有点难想,要把资源串和病毒串一起建到Trie图上(节点数这时是6W了,而不是5W)。要充分理解题目所给的,资源串不包含病毒串这个条件。由n的范围1-10,很容易想到是用状态压缩dp去接字符串,关键是怎么定义状态,这里我就把我的状态定义先给出来,用dp[i][j]表示j状态,以第i个字符串结尾的最小长度。往下接一个k的话,我们要知道j跟k相接时,不包含病毒串能接出的最小长度是多少。那么,我们就预先要处理出len[i][j],表示对于i资源串后面接j时,不包含病毒串,能接出的最小长度是多少。对于所有的i,找到它所在的节点,做一次spfa,就能得到它到所有节点的最短距离,那么与j相接的最短要增加的长度就是i所在的节点到j所在的节点的最短距离,这个复杂度是10*6w左右,还是可以接受的。最后就是状态压缩dp推一下了,这个还是蛮简单的。
#include<stdio.h> #include<string.h> #include<algorithm> #include<queue> using namespace std ; const int maxn = 66666 ; const int INF = maxn<<2 ; int pos[15] , cnt ; int dp[12][1<<12] ; int min ( int a , int b ) { return a < b ? a : b ; } struct Que { int tail , head ; int q[100005] ; void init () { head = 1 , tail = 0 ; } bool empty () { return head > tail ; } int front () { return q[head] ; } void pop () { head ++ ; } void push ( int x ) { q[++tail] = x ; } } Q , Q1 ; class AC_auto { private : int tot , c[2][maxn] , id[maxn] , fail[maxn] ; int dis[maxn] , len[15][15] , vis[maxn] , fuck[maxn] ; int new_node () { fail[tot] = id[tot] = 0 ; c[0][tot] = c[1][tot] = 0 ; return tot ++ ; } public : void init () { tot = cnt = 0 ; Q.init () ; new_node () ; } void insert ( char *s , int v , int flag ) { int now = 0 ; for ( ; *s ; s ++ ) { int k = *s - '0' ; if ( !c[k][now] ) c[k][now] = new_node () ; now = c[k][now] ; } if ( flag ) pos[++cnt] = now , fuck[now] = v ; else id[now] = 1 ; } void get_fail () { int i , u = 0 , j , e ; for ( i = 0 ; i < 2 ; i ++ ) if ( c[i][u] ) Q.push ( c[i][u] ) ; while ( !Q.empty () ) { u = Q.front () ; Q.pop () ; for ( i = 0 ; i < 2 ; i ++ ) { if ( c[i][u] ) { e = c[i][u] ; fail[e] = c[i][fail[u]] ; id[e] |= id[fail[e]] ; Q.push ( e ) ; } else c[i][u] = c[i][fail[u]] ; } } } void spfa ( int u ) { int i , j , e ; for ( i = 0 ; i <= tot ; i ++ ) vis[i] = 0 , dis[i] = INF ; Q1.init () ; Q1.push ( u ) , vis[u] = 1 ; dis[u] = 0 ; while ( !Q1.empty () ) { u = Q1.front () ; Q1.pop () ; vis[u] = 0 ; for ( i = 0 ; i < 2 ; i ++ ) { e = c[i][u] ; if ( !id[e] && dis[e] > dis[u] + 1 ) { dis[e] = dis[u] + 1 ; if ( !vis[e] ) { vis[e] = 1 ; Q1.push ( e ) ; } } } } } void init ( int maxl ) { int i , j ; for ( i = 0 ; i <= cnt ; i ++ ) for ( j = 0 ; j <= cnt ; j ++ ) len[i][j] = INF ; for ( i = 0 ; i <= cnt ; i ++ ) for ( j = 0 ; j <= maxl ; j ++ ) dp[i][j] = INF ; for ( i = 1 ; i <= cnt ; i ++ ) dp[i][1<<(i-1)] = fuck[pos[i]] ; } void DP ( int maxl ) { int i , j , k ; for ( i = 0 ; i <= maxl ; i ++ ) for ( j = 1 ; j <= cnt ; j ++ ) if ( i & ( 1 << ( j - 1 ) ) ) { for ( k = 1 ; k <= cnt ; k ++ ) if ( ! ( i & ( 1 << ( k - 1 ) ) ) ) dp[k][i|(1<<(k-1))] = min ( dp[k][i|(1<<(k-1))] , dp[j][i] + len[j][k] ) ; } } int work () { int i , j , k , l ; int maxl = ( 1 << cnt ) - 1 ; init ( maxl ) ; for ( i = 1 ; i <= cnt ; i ++ ) { spfa ( pos[i] ) ; for ( j = 1 ; j <= cnt ; j ++ ) len[i][j] = dis[pos[j]] ; } DP ( maxl ) ; int ans = INF ; for ( i = 1 ; i <= cnt ; i ++ ) ans = min ( ans , dp[i][maxl] ) ; return ans ; } } ac ; char s[1111] ; int main () { int n , m ; while ( scanf ( "%d%d" , &n , &m ) != EOF ) { if ( n == 0 && m == 0 ) break ; ac.init () ; while ( n -- ) { scanf ( "%s" , s ) ; int l = strlen ( s ) ; ac.insert ( s , l , 1 ) ; } while ( m -- ) { scanf ( "%s" , s ) ; ac.insert ( s , 0 , 0 ) ; } ac.get_fail () ; printf ( "%d\n" , ac.work () ) ; } } /* 2 3 10101 00011 000110101 0001110101 1010100011 2 1 101101 10111 11011 11 10 */