POJ 3123 Ticket to Ride 动态规划+Minimal Steiner Tree

POJ 3123 Ticket to Ride 动态规划+Minimal Steiner Tree

这题绝对不是盖的。
题目大意是:
给出一个无向图,和四对数据。每对数据分别为图中的两个点。
要求添加一些边,使每对点都能连通,让总边权最小。

首先考虑一个子问题:指定一些点,添加一些边,让它们连通,并且总边权最小。
这个问题就是Minimal Steiner Tree问题,解决方法可以见 这里。
这问题不是盖的,它居然是NP完全问题。。
汗。。今天终于在POJ见识到啥叫NP完全问题了。。

大的问题可以分为多个子问题。可以枚举所有pair的连接状况。
比如说 {1和2链接,3和4链接} 或者 {1独立,2独立,3和4链接} 等等
一共有15种情况。分别为
    // 1,1,1,1
    {{1},{2},{3},{4}},
    // 1,1,2
    {{1,2},{3},{4}},
    {{1,3},{2},{4}},
    {{1,4},{2},{3}},
    {{2,3},{1},{4}},
    {{2,4},{1},{3}},
    {{3,4},{1},{2}},
    // 2,2
    {{1,2},{3,4}},
    {{1,3},{2,4}},
    {{1,4},{2,3}},
    // 1,3
    {{1,2,3},{4}},
    {{1,2,4},{3}},
    {{1,3,4},{2}},
    {{2,3,4},{1}},
    // 4
    {{1,2,3,4}},

其中有一些是重复的,就可以开一个数组保存下来。
贴一个我的程序。当然,这个是TLE的。。官方的数据需要将近一分钟才能跑完。
另外一个标程运行飞快,用得是更好的方法,点 这里。


#include  < stdio.h >
#include 
< string .h >
#include 
< algorithm >
#include 
< cmath >

using   namespace  std;

char  names[ 32 ][ 32 ];
int  N, M;
int  W[ 32 ][ 32 ];
const   int  INF  =   10032 * 32 ;
int  pairs[ 4 ];
int  dp[ 256 ][ 2 ], dn;

int  getcity( char   * s)
{
    
int  i;
    
for  (i  =   0 ; i  <  N; i ++ )
        
if  ( ! strcmp(s, names[i]))
            
break ;
    
return  i;
}

int  prim( int  mask)
{
    
int  i, j, mc, mi, a, c, t;

    c 
=   0 ;
    
for  (i  =   0 ; i  <  N; i ++
        
if  (mask  &  ( 1   <<  i)) {
            a 
=   1   <<  i;
            c
++ ;
        }

    t 
=   0 ;
    
while  ( -- c) {
        mc 
=  INF;
        
for  (i  =   0 ; i  <  N; i ++ )
            
if  (a  &  ( 1   <<  i)) 
                
for  (j  =   0 ; j  <  N; j ++ )
                    
if  (((mask  ^  a)  &  ( 1   <<  j))  &&  W[i][j]  <  mc) {
                        mc 
=  W[i][j];
                        mi 
=  j;
                    }
        
if  (mc  ==  INF)
            
return  INF;
        a 
|=   1   <<  mi;
        t 
+=  mc;
    }

    
return  t;
}

int  K;

int  dfs( int  start,  int  mask,  int  n)
{
    
int  i, r;

    
if  (n  >=  K  -   2 )
        
return  prim(mask);
    
    r 
=  prim(mask);
    
for  (i  =  start; i  <  N; i ++
        
if  (( 1   <<  i)  &   ~ mask) 
            r 
=  min(r, dfs(i + 1 , mask | ( 1 << i), n + 1 ));

    
return  r;
}

int  minicost( int  mask)
{
    
int  i, r;

    
for  (i  =   0 ; i  <  dn; i ++ )
        
if  (mask  ==  dp[i][ 0 ])
            
return  dp[i][ 1 ];

    K 
=   0 ;
    
for  (i  =   0 ; i  <  N; i ++ )
        
if  (mask  &  ( 1   <<  i))
            K
++ ;
    
    r 
=  dfs( 0 , mask,  0 );

    dp[dn][
0 =  mask;
    dp[dn][
1 =  r;
    dn
++ ;
    
return  r;
}

int  stats[ 15 ][ 8 ][ 8 =  {
    
//  1,1,1,1
    {{ 1 },{ 2 },{ 3 },{ 4 }},
    
//  1,1,2
    {{ 1 , 2 },{ 3 },{ 4 }},
    {{
1 , 3 },{ 2 },{ 4 }},
    {{
1 , 4 },{ 2 },{ 3 }},
    {{
2 , 3 },{ 1 },{ 4 }},
    {{
2 , 4 },{ 1 },{ 3 }},
    {{
3 , 4 },{ 1 },{ 2 }},
    
//  2,2
    {{ 1 , 2 },{ 3 , 4 }},
    {{
1 , 3 },{ 2 , 4 }},
    {{
1 , 4 },{ 2 , 3 }},
    
//  1,3
    {{ 1 , 2 , 3 },{ 4 }},
    {{
1 , 2 , 4 },{ 3 }},
    {{
1 , 3 , 4 },{ 2 }},
    {{
2 , 3 , 4 },{ 1 }},
    
//  4
    {{ 1 , 2 , 3 , 4 }},
};

int  main()
{
    
int  i, j, k, a, b, c, ans;
    
char  sa[ 32 ], sb[ 32 ];

    
while  (scanf( " %d%d " & N,  & M), N) {
        
for  (i  =   0 ; i  <  N; i ++ )
            scanf(
" %s " , names[i]);
        
for  (i  =   0 ; i  <  N; i ++ )
            
for  (j  =   0 ; j  <  N; j ++ )
                W[i][j] 
=  INF;
        
for  (i  =   0 ; i  <  M; i ++ ) {
            scanf(
" %s%s%d " , sa, sb,  & c);
            a 
=  getcity(sa);
            b 
=  getcity(sb);
            W[a][b] 
=  W[b][a]  =  min(W[a][b], c);
        }
        
for  (i  =   0 ; i  <   4 ; i ++ ) {
            scanf(
" %s%s " , sa, sb);
            a 
=  getcity(sa);
            b 
=  getcity(sb);
            pairs[i] 
=  ( 1   <<  a)  |  ( 1   <<  b);
        }

        
//  floyd
         for  (k  =   0 ; k  <  N; k ++ )
            
for  (i  =   0 ; i  <  N; i ++ )
                
for  (j  =   0 ; j  <  N; j ++ )
                    W[i][j] 
=  min(W[i][k]  +  W[k][j], W[i][j]);

        dn 
=   0 ;
        ans 
=  INF;
        
for  (i  =   0 ; i  <   15 ; i ++ ) {
            c 
=   0 ;
            
for  (j  =   0 ; stats[i][j][ 0 ]; j ++ ) {
                a 
=   0 ;
                
for  (k  =   0 ; stats[i][j][k]; k ++ )
                    a 
|=  pairs[stats[i][j][k]  -   1 ];
                c 
+=  minicost(a);
            }
            ans 
=  min(ans, c);
        }

        printf(
" %d\n " , ans);
    }
    
return   0 ;
}



 

你可能感兴趣的:(POJ 3123 Ticket to Ride 动态规划+Minimal Steiner Tree)