poj 1699 Best Sequence (压缩dp)

先说一下我了解的压缩状态dp吧。

经典的压缩dp问题是TSP问题: 一个n个点的带权的有向图,求一条路径,使得这条路经过每个点恰好一次,并且路径上边的权值和最小(或者最大)。或者求一条具有这样性质的回路。

压缩dp问题中存储点集是利用二进制形式,比如 3 = 0000,0000,0000,0011 表示第一个和第二个点在点集中,5 = 0000,0000,0000,0101 表示第一个和第三个点在点集中,以此类推。

呃,简单来说,就是用一个数表示一种状态,一般n<= 16 或n <= 32 。

状态转移:如果状态存在,dp[i][j] = 0 ,否则为无穷大.

1、状态存在:dp[i][j] = min(dp[k][s] + v[s][j] );k表示i集合中去掉了j点的集合,s遍历集合k中的点并且dp[k][s]状态存在,点s到点j有边存在,v[s][j]表示边的权值。

2、状态不存在,则为无穷大。       最后的结果是: min( dp[( 1<<n ) – 1][j] ) ( 0 <= j < n )

利用二进制还有一个好处,就是对集合的一些操作可以用位运算来实现,  例如从集合i中去掉点j:  k = i & (~( 1<<j)) 或者 k = i - (1<<j)

好吧,现在来说说这题;

题意:给出N个任意长度的字符串,一些字符串的首尾有些字符时相同的,可以相互重叠起来缩短长度,让你求把这N个字符串连起来的最短的长度。

思路:听说这题可以暴力过,不过没试过,可以求出两个字符串的最长重叠长度,然后全排列。还有好多人用搜索过的,不过我是用的压缩dp,先预处理一下字符串,求出任意两个字符串相连的最大重叠长度,然后用一下压缩dp救过了。

代码:

View Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#define  N 24
#define  M 12
using namespace std ;

int att[N][N] , len[M] , dp[1<<M][N] ;
char str[M][N] ;
int n ;

void cal ( int x , int y )
{
    int i , j , k ;
    int len1 = len[x] ;
    int len2 = len[y] ;
    int lenx = len1 < len2 ? len1 : len2 ;
    for ( i = lenx - 1 ; i >= 0 ; i-- )
    {
        for ( j = len1 - i - 1 , k = 0 ; j < len1 ; j++ , k++ )
        if ( str[x][j] != str[y][k] )
        break;
        if ( k == i + 1 )
        {
            att[x][y] = k ;
            return ;
            break;
        }
    }
    att[x][y] = 0 ;
}

int main()
{
    int cas , i , j , k ;

    scanf( "%d" , &cas );
    while( cas-- )
    {
        scanf( "%d" , &n );
        getchar();
        for( i = 0 ; i < n ; i++ )
        {
            scanf( "%s" , str[i] );
            getchar();
            //求出每个字符串长度
            len[i] = strlen( str[i] );
        }
        //预处理,算出任意两个字符串重叠的最大长度
        for ( i = 0 ; i < n ; i++ )
        {
            for ( j = 0 ; j < n ; j++ )
            if ( i != j )
            cal ( i , j );
        }
        memset( dp , -1 , sizeof( dp ));
        //压缩状态dp
        for ( i = 0 ; i < n ; i++ )
        dp[1<<i][i] = len[i] ;
        int np = ( 1<<n ) - 1 ;
        for ( i = 1 ; i <= np ; i++ )
        {
            for( j = 0 ; j < n ; j++ )
            {
                if ( dp[i][j] == -1 || ( i&( 1<<j )) == 0 )
                continue ;
                for ( k = 0 ; k < n ; k++ )
                {
                    if ( j == k )
                    continue ;
                    if ( i&( 1<<k ))
                    continue ;
                    int tem = i|(1<<k);
                    if ( dp[tem][k] == -1 || dp[i][j] - att[j][k] + len[k] < dp[tem][k] )
                    dp[tem][k] = dp[i][j] - att[j][k] + len[k] ;
                }
            }
        }
        int minx = -1 ;
        for ( i = 0 ; i < n ; i++ )
        if( minx == -1 || dp[np][i] < minx )
        minx = dp[np][i] ;
        printf( "%d\n" , minx );
    }
    return 0 ;
}

 

 

posted @ 2012-08-08 11:31  Misty_1  阅读(263)  评论(0编辑  收藏  举报