有点难想,要把资源串和病毒串一起建到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
*/
本文详细解析了HDU3247 ResourceArchiver这道题目的解题思路,采用状态压缩动态规划结合Trie树的方法解决资源串与病毒串匹配的问题。文章介绍了如何通过SPFA算法预处理最短路径来辅助状态转移,实现高效的求解过程。
484

被折叠的 条评论
为什么被折叠?



