源哥每日一题第十三弹 百练4124:海贼王之伟大航路 状压dp

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

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

思路:先想一个不太成熟的思路。用dp[s][j]表示。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;
}

原文地址:https://www.cnblogs.com/fengyuzhicheng/p/9153254.html

时间: 2024-10-13 01:58:14

源哥每日一题第十三弹 百练4124:海贼王之伟大航路 状压dp的相关文章

源哥每日一题第20弹 poj 1272 还是冰茶集

连接:http://acm.hdu.edu.cn/showproblem.php?pid=1272 题意:要 要 要 要啥题意啊就是问题这个东西是不是一颗树 解决方式有很多种,说是冰茶集,但是你用别的方法也可以做啊~ 思路:首先作为一棵树一定满足顶点数等于边数+1,但只满足了这个条件也不行(脑补一个圆)判断方法:比方说a和b俩节点,他俩爹要是一样,你再把a和b连上,那不就有环了吗 p.s. 我记得这个题在hdu上会爆栈来着-- 还想硬广一下 #pragma comment(linker, "/S

源哥每日一题第十四弹 hdu 1565 还是状压dp

题目:http://acm.hdu.edu.cn/showproblem.php?pid=1565 题意:自己念 分析:有了上一题的基础,这题的状态方程也好想:dp[i][s]表示第i行第s个状态取得的最大值. 可以预处理出每一行每一种状态所能获得的值,方程就是dp[i][j] = max(dp[i][j],dp[i-1][k]+sum[i][j]); 要注意的就是判断相邻的情况:两个状态,如果a&b!=0则表示它们有相重叠的区域. #include <bits/stdc++.h> u

源哥每日一题第十八弹 poj 1182 并查集

题目链接:http://poj.org/problem?id=1182 题意:看不懂?退群吧 比平常的并查集加了一个判断集合间关系的操作: 开一个数组记录当前点所在集合的次序(第几个集合)用012表示 比较简单的思路,不过体现了并查集的精妙 #include <iostream> #include <cstring> #include <cstdio> using namespace std; int dad[500005]; int st[50005]; int fi

源哥每日一题第十九弹 poj 2236 还是冰茶集

连接:http://poj.org/problem?id=2236 题意:有一堆坏电脑,和两种操作:O表示修好这台电脑,S 询问两台电脑是否联通.主要是判电脑是否联通:两台电脑间距离小于d就是联通的. 题解:emmm--直接写啊 #include <iostream> #include <cstdio> using namespace std; int n; long long d; int pre[1005]; int jud[1005]; struct coor { long

POJ 2411 &amp;&amp; HDU 1400 Mondriaan&#39;s Dream (状压dp 经典题)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12341   Accepted: 7204 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

刷题总结——bzoj1725(状压dp)

题目: 题目描述 Farmer John 新买了一块长方形的牧场,这块牧场被划分成 N 行 M 列(1<=M<=12; 1<=N<=12),每一格都是一块正方形的土地. FJ 打算在牧场上的某几格土地里种上美味的草,供他的奶牛们享用.遗憾的是,有些土地相当的贫瘠,不能用来放牧.并且,奶牛们喜欢独占一块草地的感觉,于是 FJ 不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边.当然,FJ 还没有决定在哪些土地上种草. 作为一个好奇的农场主,FJ 想知道,如果不考虑草地的总块数,

刷题向》关于第一篇状压DP BZOJ1087 (EASY+)

这是本蒟蒻做的第一篇状压DP,有纪念意义. 这道题题目对状压DP十分友善,算是一道模板题. 分析题目,我们发现可以用0和1代表每一个格子的国王情况, 题目所说国王不能相邻放置,那么首先对于每一行是否合法的判断条件就出来了:就是对于情况X,如果X&(x<<1)==0,即为合法情况. 同理这样我们就可以得出每一行对于上一行是否合法的条件:(x&y)==0&&(x&(y<<1))==0&&(x&(y>>1))==

P3694 邦邦的大合唱站队/签到题(状压dp)

P3694 邦邦的大合唱站队/签到题 题目背景 BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. 题目描述 N个偶像排成一列,他们来自M个不同的乐队.每个团队至少有一个偶像. 现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起.重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意. 请问最少让多少偶像出列? 输入输出格式 输入格式: 第一行2个整数N,M. 接下来N个行,每行一个整数a_i(1\le a_i

Orz_panda cup I题 (xdoj1117) 状压dp

Orz_panda cup I题 (xdoj1117)  状压dp 1117: Insert Orz Pandas 时间限制: 2 Sec  内存限制: 128 MB提交: 15  解决: 5[提交][状态][讨论版] 题目描述 Orz panda emotion is a famous emotion in XDU/ACM-ICPC QQ groups.Big Big Xi loves to create new Orz panda emotions.Now he has a matrix w