zoukankan      html  css  js  c++  java
  • 源哥每日一题第十三弹 百练4124:海贼王之伟大航路 状压dp

    连接:http://bailian.openjudge.cn/practice/4124

    题意:从1到n走过所有点恰好一次最短时间。乱搞的话会完美的超时(阶乘级别的复杂度,虽然范围很小,但是也足够超时了)。

    思路:先想一个不太成熟的思路。用dp[j][s]表示。s记录的是每个点是否被走过的状态。而dp[s][j]表示的是从1走到j状态所用的最小时间。这样的思路成不成立呢?首先,考虑初始值。开始是在1号点,那么dp[1][1]自然就是0了,其他就是max;另外,题面说只要遍历每一个点,而于顺序的话,并没有要求,也就是说,通过任何一种演变方式到达该状态的方式都是等价的,满足了动态规划无后效性的要求。同时,对于每一个时刻,当前位置存储的值都是当前最优解,既问题具有最优子结构性质。同时,对于每一个演变,我们可以在dp[s][j]的基础上,推出当前状态的值可以通过上一步演变就到达的状态进行更新,这也就是所谓“人人为我”的过程。dp方程也好想,既:

    dp[i][k] = min(dp[i][k],dp[i_pre][k_pre]+G[i_pre][i]);

    接下来就是比较重要的问题了:如何表示这些状态,以及进行状态之间的计算呢?

    用16个bool?太麻烦了!换一种思路:因为只有16个数,将他们编成二进制编码101010101010100……每一位代表当前位置所代表的点是否被走过,这样的话,只需要2^16个无符号shortint(实际用int)就可以表示所有可能的状态啦。

    第一个问题是解决了,可如何进行状态间的变换呢?请把c语言程序设计翻到xxx页,有关位运算的章节:

    &, 这个东西叫按位与,既每一位依次比较一样就是1,不一样就是0。平时常用的判断奇偶性的n&1就是最简单的应用。

    |,这东西叫按位或,键位有点怪,一般在enter附近,意思是每一位依次比较有1就是1,全0就是0

    ^,按位异或,也是中文输入法下省略号的打法。官方的话是相同为0,不同为1,我的理解就是不带进位的加法。

    ~,取反,在tab上面,int下的话就是~x = -x-1 最常用的那个while(~scanf)用的就是这个原理(~0 = -0-1 = -1 = EOF)。

    << >> 左移右移 不多说,乘2除2

    一些比较清奇的用法

    从低位到高位,取n的第m位

    return (n >> (m-1)) & 1;
    从低位到高位.将n的第m位置1
    return n | (1 << (m-1));
    从低位到高位,将n的第m位置0

    return n & ~(1 << (m-1));

    (x&-x)只保留最低位的1

    具体用法的话读代码,体会一下:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    using namespace std;
    
    void read(){
    #ifndef ONLINE_JUDGE
        freopen("D:\fengyu\Jiang_C\.vscode\in.txt","r",stdin);
        freopen("D:\fengyu\Jiang_C\.vscode\out.txt","w",stdout);
    #endif
    }
    int G[20][20];
    int dp[20][(1<<16)+5];
    int main() { read();
        int n;
        while (cin >> n) {
            memset(dp,0x3f,sizeof(dp));
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    cin >> G[i][j];
                }
            }
            dp[1][1] = 0;
            int ans = (1<<n)-1;
            for (int k = 1; k <= ans; k++) {
                for (int i = 1,_i = 1; i <= n; i++,_i<<=1) {
                    if (k&_i)
                    for (int j = 1, _j = 1; j <= n; j++,_j<<=1) {
                        if (i!=j && k&_j)
                            dp[i][k] = min(dp[i][k],dp[j][k^_i]+G[j][i]);
                    }
                }
            }
            cout << dp[n][ans] << endl;
        }
        return 0;
    }
  • 相关阅读:
    char nvarchar varchar
    第32月第8天 打包 Framework 时使用 CocoaPods 引入第三方库的方法
    第31月第25天 xcode debug 限制uitextfiled输入
    第31月第22天 draw
    第31月第19天 NV12
    第31月第17天 resolveInstanceMethod
    第31月第15天 -fembed-bitcode
    第31月第10天 tableview头部空白 Other Linker Flags rtmp
    第31月 第9天 责任链AppDelegate
    第30月第18天 autolayout代码
  • 原文地址:https://www.cnblogs.com/fengyuzhicheng/p/9153254.html
Copyright © 2011-2022 走看看