美文网首页动态规划
GDINOJ1044--状态压缩动态规划

GDINOJ1044--状态压缩动态规划

作者: 小胖子善轩 | 来源:发表于2017-08-20 12:43 被阅读104次

    无论多忙都好,每日都要进行数学思维和计算机思维的锻炼,才能让自己做事高效且思维敏捷。


    题目(GDINOJ)

    http://114.215.99.34/#/enter/problem?pid=1044
    春游的时间到了,CC想要利用这个难得的好时间去旅游。
    CC有N个想要去的旅游景点,他想要在这个寒假全部都去玩一次,经过他的调查,他已经得到了任何两个旅游景点之间的路费。所以他想让你帮他设计一条旅行路线,使得最终的费用最小。

    分析

    这是一个很经典的TSP问题了,NP难的一个问题。因为时间复杂度是N!,所以这里输入上限是20。虽然只有20个城市,但是时间复杂度依然相当高,常规算法依然十分耗时。(现在已经有很好地启发式算法的方案来解决了,这里不展开讨论,这里讨论的是常规算法)

    求解

    我们采用动态规划来求解,动态规划最难的地方在于如何证明子问题具有全局最优,状态的定义和状态转移方程的设定。当然了,编程实现也不容易。

    状态定义

    我们先寻求一下最优解子结构,我们先定义一个状态dp(i,j),其中i表示从旅行到了第i点,j是一个集合,表示未旅行的点的集合。所以,状态dp(i,j)就表示从起点开始旅行到了第i点,还有集合J的点没有旅行的最少费用。

    状态转移

    dp(i,j) = min( dp(i,j) , dp(i, j - k) + G[i][k]) (k 属于集合k),状态方程其实也比较容易理解,就是尝试每一种未旅行的城市,并且记录下来。

    实现细节
    1. 单纯看转移方程,时间复杂度是n!的。但是DP的核心是打表,所以记录过的就不要再跑了。
    2. 如何来表示集合J呢?这里运用了状态压缩的技巧,就是把城市用二级制来表示,这里注意爆内存。
    代码
    #include <string.h>
    #include <math.h>
    
    int F[30][30];
    
    const int INF = 99999999 ;
    const int MAX = (1<<20)+1 ;
    int dp[21][MAX];
    int dis[30][30] ;
    
    int min(int a, int b) {
        return a<b?a:b ;
    }
    int n ;
    
    int InitState() {
        int res = 0 ;
        for( int i = 1 ; i<=n ;i++ ) {
            res = res| (1<<(i-1));
        }
        return res ;
    }
    
    int GetState(int State, int des) {
        State = State^(1<<(des-1)) ;
        return State ;
    }
    
    void DP(int State, int des) {
        int& Temp = dp[des][State];
        if (Temp != INF)    return ;
        if( State == 0 ) {
            Temp = F[des][0] ;
            return ;
        } else {
            for( int i = 1 ; i<= n ;i++ ) {
                if( State & (1<<(i-1)) ) {
                    int NewState = GetState(State,i);
                    DP(NewState, i);
                    Temp = min(Temp, dp[i][NewState]+F[des][i]);
                }
            }
        }
    }
    
    int main()
    {
        // freopen("input.in","r",stdin);
        while(scanf("%d",&n) != EOF) {
            memset( dp , 0 , sizeof(dp) ) ;
            for( int i = 1 ; i<=n ;i++ ){scanf("%d",&F[0][i]); F[i][0] = F[0][i]; } 
            for( int i = 1 ; i<=n ; i++) {
                for( int j = 1 ; j<=n ; j++ ) {
                    scanf("%d",&F[i][j]);
                }
            }
            int All = (1<<n) - 1;
            //init
            // printf("All: %d\n",All);
            for( int i = 0 ; i<=n ; i++ )
                for( int j = 0 ; j<=All ; j++)
                    dp[i][j] = INF ;
            All = InitState();
            DP(All,0);
            printf("%d\n",dp[0][All]) ;
        }
        return 0;
    }
    
    

    感悟

    虽然这题大二的时候就做过了。。
    我只是想试试步入了社会之后是否能沉下心来进行数学,算法和英语的训练呢?坚持它一年试试吧。。。

    2017-8-20

    相关文章

      网友评论

        本文标题:GDINOJ1044--状态压缩动态规划

        本文链接:https://www.haomeiwen.com/subject/ykhfdxtx.html