在 n×n 的棋盘上放 k 个国王,国王可攻击相邻的 8 个格子,求使它们无法互相攻击的方案总数。
输入格式
共一行,包含两个整数 n 和 k。
输出格式
共一行,表示方案总数,若不能够放置则输出0。
数据范围
1 ≤ n ≤ 10,
0 ≤ k ≤ n2
输入样例:
3 2
输出样例:
16
算法解析
算法构造
这道题目,根据数据范围,不难得出,这道题目考察的是状态压缩动态规划。
分析题目,我们可以得到如下信息。
那么,我们接下来,思考两个流程。
表示状态
显然,题目给的条件,是国王总数是严格限制的,就是k个。
所以说,我们放置了多少个国王,是需要考虑的。
接着,根据棋盘类型的状态压缩动态规划的套路,每一行的状态,我们需要明白。
也就是每一行,哪些位置放了国王。
综上所述,我们可以得出,动态规划的状态表示。
f[i][j][s]为所有只摆在前 i 行,目前摆了 j 个国王,而且第 i 行的摆放状态为 s
我们可以举一个例子
n=5
f[1][2][20]表示第一行,已经摆了两个国王,摆在左边第一个,和左边第三个
(20)10=(10100)2
状态转移
在这里,状态之间的转移,必然要满足,国王之间不会相互攻击到,那么我们进行分析。
两个国王,如果他们存在,直接靠近(上下左右)或者简介靠近(两斜对角),那么显然是不合法的。
因此,转换成为状态理解。
对于一个状态集合而言,显然不能存在相邻的1.
101(可以)两个国王有间隔
110(不可以)国王1和国王2相邻,可以相互攻击
因为这会导致,左右两个国王相邻,然后发起攻击。
而且,对于上下两行而言,不能有共同的一位有1
101
101
因为这会导致,上下两个国王相邻,然后发起攻击。
我们讨论完了,上下左右,接下来是最难的两斜对角。
我们设,第 i 行的状态为 a,第 i+1 行状态为 b
那么
S = a 或 b 也就是 S = a|b
是不可以存在,有相邻的1的。
a=100
b=010
S=110
因此这会导致,两斜对角国王相互攻击。
综上所述,我们得到集合转移的约束条件。
#include
#include
using namespace std;
typedef long long LL;
const int N = 12, M = 1 << 10, K = 110;
int n, m;
vector<int> state; // 存储合法状态
int cnt[M]; // 存储每种状态1的数量
vector<int> head[M]; // 没种状态的状态转移
LL f[N][K][M]; // 所有只摆在前i行,目前摆了j个国王,而且第i行的摆放状态为s
bool check(int state)
{
for (int i = 0; i < n; i ++ )
if ((state >> i & 1) && (state >> i + 1 & 1))
return false;
return true;
}
int count(int state)
{
int res = 0;
for (int i = 0; i < n; i ++ ) res += state >> i & 1;
return res;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < 1 << n; i ++ )
if (check(i)) { // 检查当前状态的合法性
state.push_back(i);
cnt[i] = count(i);
}
for (int i = 0; i < state.size(); i ++ )
for (int j = 0; j < state.size(); j ++ ) {
// 不同状态直接关系
int a = state[i], b = state[j];
if ((a & b) == 0 && check(a | b)) // 上下 斜对角
head[i].push_back(j);
}
f[0][0][0] = 1;
for (int i = 1; i <= n + 1; i ++ )
for (int j = 0; j <= m; j ++ )
for (int a = 0; a < state.size(); a ++ )
for (int b : head[a]) {
int c = cnt[state[a]];
if (j >= c)
f[i][j][a] += f[i - 1][j - c][b];
}
cout << f[n + 1][m][0];
return 0;
}
笔记学习:
作者:秦淮岸灯火阑珊
链接:https://www.acwing.com/solution/content/10007/
来源:AcWing
农夫约翰的土地由 M×N 个小方格组成,现在他要在土地里种植玉米。
非常遗憾,部分土地是不育的,无法种植。
而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。
现在给定土地的大小,请你求出共有多少种种植方法。
土地上什么都不种也算一种方法。
输入格式
第 1 行包含两个整数 M 和 N。
第 2…M+1 行:每行包含 N 个整数 0 或 1,用来描述整个土地的状况,1 表示该块土地肥沃,0 表示该块土地不育。
输出格式
输出总种植方法对 108 取模后的值。
数据范围
1 ≤ M,N ≤ 12
输入样例:
2 3
1 1 1
0 1 0
输出样例:
9
先求出一行中所有可能出现的状态,状态适用于每一行,然后遍历每一行、每一列,枚举全部状态可能性,根据题目判断全部合法的状态转移,然后存储下来。
算法解析
算法构造
经典的棋盘型状态压缩动态规划,我们可以按照之前小国王的思路,处理本题。
首先,我们需要明确,题目的要求:
状态设计
首先,我们得明确状态是什么。
我们这个状态,肯定是要统计方案数。
我们这个状态,必然需要表示每一行土地种植的状态。
因此得到:
f[i][s]表示已经种植前i行,且第i行种植的状态为s的方案数
状态转移
题目的限制条件,其实就是我们转移的限制条件。
我们知道,这里是十字形的禁止种植,也就是上下左右不能有相邻的两棵玉米。
那么怎么判断呢?
如果说我们把1表示这个地方种植玉米,0表示不种植
S=11101,2,3这三个地方种玉米,第四个地方不种植玉米
对于一行而言,不能种植相邻的玉米。
即:
对于一行而言,不能有相邻的1
S=1110是不合法的状态
对于相邻的两行而言,不能在同一列都种植玉米
a=1010
b=1000
这是不可以的,在第一个位置会出现上下矛盾
那么我们可以转化为:
a&b==0
最后,对于题目中的土地不能种植,我们可以认为。
如果第i行的状态为s,那么荒废土地处不能有1
我们可以设计一个数组:
g[i]表示第i行不能种植土地的状态
g[1]=1011表示第一行,第一个,第三个,第四个位置不能种植玉米
总而言之
第i行的状态为s
那么s&g[i]==0
#include
#include
using namespace std;
const int N = 14, M = 1 << 12, mod = 1e8;
int n, m;
int w[N];
vector<int> state;
vector<int> head[M];
int f[N][M]; // 前i行
bool check(int state)
{
for (int i = 0; i + 1 < m; i ++ )
if ((state >> i & 1) && (state >> i + 1 & 1))
return false;
return true;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 0; j < m; j ++ ) {
int t;
cin >> t;
w[i] += !t * (1 << j); // 存储农田状态,非运算方便后续判断状态
}
for (int i = 0; i < 1 << m; i ++ ) // 一行中每一列的状态
if (check(i))
state.push_back(i);
for (int i = 0; i < state.size(); i ++ )
for (int j = 0; j < state.size(); j ++ ) {
int a = state[i], b = state[j];
if (!(a & b)) // 相邻两行的合法状态转移
head[i].push_back(j);
}
f[0][0] = 1;
for (int i = 1; i <= n + 1; i ++ )
for (int j = 0; j < state.size(); j ++ )
if (!(state[j] & w[i])) // 当前状态针对当前行情况合法
for (int k : head[j])
f[i][j] = (f[i][j] + f[i - 1][k]) % mod; // 状态转移
cout << f[n + 1][0]; // 始终多一行,状态不存数据,仅存储由之前状态转移来的总数据
return 0;
}
笔记学习:
作者:秦淮岸灯火阑珊
链接:https://www.acwing.com/solution/content/17569/
来源:AcWing
司令部的将军们打算在 N×M 的网格地图上部署他们的炮兵部队。
一个 N×M 的地图由 N 行 M 列组成,地图的每一格可能是山地(用 H 表示),也可能是平原(用 P 表示),如下图。
在每一格平原地形上最多可以布置一支炮兵部队(山地上不能够部署炮兵部队);一支炮兵部队在地图上的攻击范围如图中黑色区域所示:
如果在地图中的灰色所标识的平原上部署一支炮兵部队,则图中的黑色的网格表示它能够攻击到的区域:沿横向左右各两格,沿纵向上下各两格。
图上其它白色网格均攻击不到。
从图上可见炮兵的攻击范围不受地形的影响。
现在,将军们规划如何部署炮兵部队,在防止误伤的前提下(保证任何两支炮兵部队之间不能互相攻击,即任何一支炮兵部队都不在其他支炮兵部队的攻击范围内),在整个地图区域内最多能够摆放多少我军的炮兵部队。
输入格式
第一行包含两个由空格分割开的正整数,分别表示 N 和 M;
接下来的 N 行,每一行含有连续的 M 个字符(P 或者 H),中间没有空格。按顺序表示地图中每一行的数据。
输出格式
仅一行,包含一个整数 K,表示最多能摆放的炮兵部队的数量。
数据范围
N ≤ 100,M ≤ 10
输入样例:
5 4
PHPP
PPHH
PPPP
PHPP
PHHP
输出样例:
6
因此在这两题里,我们只需压缩存储 当前层的状态 ,然后枚举 合法的上个阶段 的状态进行 转移 即可
但是本题的棋子攻击范围是 2,我们只压缩当前层一层状态后进行转移,是不能保证该次转移是 合法的
即不能排除第 i−2 层摆放的棋子可以攻击到第 i 层棋子的 不合法 情况
而解决该问题的手段就是:压缩存储两层的信息,然后枚举合法的第 i−2 层状态进行转移即可
a 为第 i 行的状态,用 for j 来枚举
b 为第i-1行的状态,用 for k 来枚举
c 为第i-2行的状态,用 for u 来枚举
f[i][j][k] 表示在前 i 行中摆放,第 i 行的状态为j,第i-1行的状态为k,的最大值
f[i-1][k][u]表示在前i-1行中摆放,第i-1行的状态为k,第i-2行的状态为u,的最大值
然后加上第i行摆放个数cnt[a]更新最大值,此题的最后一步的为第i-2行
#include
#include
using namespace std;
const int N = 10, M = 1 << 10;
int n, m;
int g[1010];
int f[2][M][M];
vector<int> state;
int cnt[M];
bool check(int state)
{
for (int i = 0; i < m; i ++ )
if ((state >> i & 1) && ((state >> i + 1 & 1) || (state >> i + 2 & 1)))
return false;
return true;
}
int count(int state)
{
int res = 0;
for (int i = 0; i < m; i ++ )
if (state >> i & 1)
res ++ ;
return res;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ )
for (int j = 0; j < m; j ++ ) {
char c;
cin >> c;
g[i] += (c == 'H') << j; // 存储矩阵状态
}
for (int i = 0; i < 1 << m; i ++ ) // 枚举一行可能的全部状态
if (check(i)) { // 判断合法
state.push_back(i); // 存储合法状态
cnt[i] = count(i); // 记录状态中的1
}
// 枚举状态转移
for (int i = 1; i <= n; i ++ )
for (int j = 0; j < state.size(); j ++ )
for (int k = 0; k < state.size(); k ++ )
for (int u = 0; u < state.size(); u ++ ) {
int a = state[j], b = state[k], c = state[u];
if (a & b | a & c | b & c) continue;
if (g[i] & b | g[i - 1] & a) continue;
// 滚动数组
// i&1 判断i是否为奇数
// n为奇数时,对应的二进制数最低位一定为1,n&1的结果就是1
// n为偶数时,相应的最低位为0,n&1的结果就是0
f[i & 1][j][k] = max(f[i & 1][j][k], f[i - 1 & 1][u][j] + cnt[b]);
}
int res = 0;
for (int i = 0; i < state.size(); i ++ )
for (int j = 0; j < state.size(); j ++ )
res = max(res, f[n & 1][i][j]);
cout << res;
return 0;
}
Kiana 最近沉迷于一款神奇的游戏无法自拔。
简单来说,这款游戏是在一个平面上进行的。
有一架弹弓位于 (0,0) 处,每次 Kiana 可以用它向第一象限发射一只红色的小鸟, 小鸟们的飞行轨迹均为形如 y=ax2+bx 的曲线,其中 a,b 是 Kiana 指定的参数,且必须满足 a<0。
当小鸟落回地面(即 x 轴)时,它就会瞬间消失。
在游戏的某个关卡里,平面的第一象限中有 n 只绿色的小猪,其中第 i 只小猪所在的坐标为 (xi,yi)。
如果某只小鸟的飞行轨迹经过了 (xi, yi),那么第 i 只小猪就会被消灭掉,同时小鸟将会沿着原先的轨迹继续飞行;
如果一只小鸟的飞行轨迹没有经过 (xi, yi),那么这只小鸟飞行的全过程就不会对第 i 只小猪产生任何影响。
例如,若两只小猪分别位于 (1,3) 和 (3,3),Kiana 可以选择发射一只飞行轨迹为 y=−x2+4x 的小鸟,这样两只小猪就会被这只小鸟一起消灭。
而这个游戏的目的,就是通过发射小鸟消灭所有的小猪。
这款神奇游戏的每个关卡对 Kiana 来说都很难,所以 Kiana 还输入了一些神秘的指令,使得自己能更轻松地完成这个这个游戏。
这些指令将在输入格式中详述。
假设这款游戏一共有 T 个关卡,现在 Kiana 想知道,对于每一个关卡,至少需要发射多少只小鸟才能消灭所有的小猪。
由于她不会算,所以希望由你告诉她。
注意:本题除 NOIP 原数据外,还包含加强数据。
输入格式
第一行包含一个正整数 T,表示游戏的关卡总数。
下面依次输入这 T 个关卡的信息。
每个关卡第一行包含两个非负整数 n,m,分别表示该关卡中的小猪数量和 Kiana 输入的神秘指令类型。
接下来的 n 行中,第 i 行包含两个正实数 (xi,yi),表示第 i 只小猪坐标为 (xi,yi),数据保证同一个关卡中不存在两只坐标完全相同的小猪。
如果 m=0,表示 Kiana 输入了一个没有任何作用的指令。
如果 m=1,则这个关卡将会满足:至多用 ⌈n/3+1⌉ 只小鸟即可消灭所有小猪。
如果 m=2,则这个关卡将会满足:一定存在一种最优解,其中有一只小鸟消灭了至少 ⌊n/3⌋ 只小猪。
保证 1 ≤ n ≤ 18,0 ≤ m ≤ 2,0 < xi,yi < 10,输入中的实数均保留到小数点后两位。
上文中,符号 ⌈c⌉ 和 ⌊c⌋ 分别表示对 c 向上取整和向下取整,例如 :⌈2.1⌉=⌈2.9⌉=⌈3.0⌉=⌊3.0⌋=⌊3.1⌋=⌊3.9⌋=3。
输出格式
对每个关卡依次输出一行答案。
输出的每一行包含一个正整数,表示相应的关卡中,消灭所有小猪最少需要的小鸟数量。
2
2 0
1.00 3.00
3.00 3.00
5 2
1.00 5.00
2.00 8.00
3.00 9.00
4.00 8.00
5.00 5.00
输出样例:
1
1
算法
(状态压缩DP) O(T(n3+n2n))
一般抛物线方程:y=ax2+bx+c
题目中的抛物线有两个特点:
因此抛物线方程为:y=ax2+bx,有两个未知数,因此两点即可确定一条抛物线。
因此最多有 n2 个不同的抛物线。接下来求出所有不同的抛物线,及其能覆盖的所有点的点集。
此时问题变成了经典的“重复覆盖问题”,即给定01矩阵,要求选择尽量少的行,将所有列覆盖住。这里标准做法是使用 Dancing Links。
但由于 n<=18,因此可以直接使用状态压缩DP求解,代码更简单。
f[i] 表示当前已经覆盖的列是i时的最小行数。
转移时随便找到当前未被覆盖的某一列 x,然后枚举所有包含 x 的行j来选择即可。
即:f[i | j] = min(f[i | j], f[i] + 1)。
时间复杂度
预处理时需要枚举所有点对来确定抛物线,然后枚举其余点是否在抛物线上,计算量是 O(n3)。
状态压缩DP的过程中,一共有 2n 个状态,每个状态需要 O(n) 的计算量,因此每个Case的时间复杂度是 O(n3+n2n),总时间复杂度是 O(T(n3+n2n))。
#include
#include
#include
#include
#define x first
#define y second
using namespace std;
typedef pair<double, double> PDD;
const int N = 18, M = 1 << 18;
const double eps = 1e-8;
int n, m;
PDD q[N];
int path[N][N]; // 表示第i个点和第j个点构成的抛物线能覆盖的点的状态
int f[M];
int cmp(double x, double y)
{
if (fabs(x - y) < eps) return 0;
if (x < y) return -1;
return 1;
}
int main()
{
int T;
cin >> T;
while (T -- ) {
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> q[i].x >> q[i].y;
memset(path, 0, sizeof path);
for (int i = 0; i < n; i ++ ) {
path[i][i] = 1 << i;
for (int j = 0; j < n; j ++ ) {
double x1 = q[i].x, y1 = q[i].y;
double x2 = q[j].x, y2 = q[j].y;
// 计算抛物线
if (!cmp(x1, x2)) continue; // 抛物线上不存在斜率为无限大的切线
double a = (y1 / x1 - y2 / x2) / (x1 - x2);
double b = y1 / x1 - a * x1;
if (cmp(a, 0) >= 0) continue; // 此题抛物线需开口向下
int state = 0;
// 计算path[i][j]的能覆盖的点的状态
for (int k = 0; k < n; k ++ ) {
double x = q[k].x, y = q[k].y;
if (!cmp(a * x * x + b * x, y)) state += 1 << k; // 这一点在这条抛物线上
}
path[i][j] = state;
}
}
memset(f, 0x3f, sizeof f);
f[0] = 0;
for (int i = 0; i + 1 < 1 << n; i ++ ) { // 遍历所有状态
int x = 0;
for (int j = 0; j < n; j ++ )
if (!(i >> j & 1)) {
x = j;
break;
}
for (int j = 0; j < n; j ++ )
f[i | path[x][j]] = min(f[i | path[x][j]], f[i] + 1);
}
cout << f[(1 << n) - 1] << endl;
}
return 0;
}
笔记、代码学习:
作者:yxc
链接:https://www.acwing.com/solution/content/4028/
来源:AcWing
求把 N×M 的棋盘分割成若干个 1×2 的的长方形,有多少种方案。
例如当 N=2,M=4 时,共有 5 种方案。当 N=2,M=3 时,共有 3 种方案。
每组测试用例占一行,包含两个整数 N 和 M。
当输入用例 N=0,M=0 时,表示输入终止,且该用例无需处理。
输出格式
每个测试用例输出一个结果,每个结果占一行。
数据范围
1 ≤ N,M ≤ 11
输入样例:
1 2
1 3
1 4
2 2
2 3
2 4
2 11
4 11
0 0
输出样例:
1
0
1
2
3
5
144
51205
如何判断当前方案是否合法?
遍历每一列,i列的方案数只和i-1列有关系
dp分析:
状态表示 f[i][j]: 前i-1列已经确定,且从第i-1列伸出的小方格在第i列的状态为j 的方案数。
属性:个数。
i=2, j=11001 表示下列图的状态
但是i-1, i列已经固定,所以集合划分是依据i-2 列伸到 i-1 列的不同状态 k 来划分
i-2 列伸到 i-1 列的状态 k=00100
状态计算:(限制条件:i-1列非空白位置可以不能放置小方格),在i列不同的放置方法就是不同的集合划分。
问题:第 i-2 列伸到 i-1 列的状态为 k , 是否能成功转移到 第 i-1 列伸到 i 列的状态为 j ?
需要满足如下条件:
f[m][0]:
列数从0开始计数,m列不放小方格,前m-1列已经完全摆放好并且不伸出来的状态
#include
#include
using namespace std;
const int N = 12, M = 1 << N;
int n, m;
long long f[N][M]; // 第i列 状态是j
bool st[M];
int main()
{
while (cin >> n >> m, n || m) {
for (int i = 0; i < 1 << n; i ++ ) { // 预处理:枚举一列的全部状态,找合法状态
int cnt = 0; // 记录一列中连续0的个数
st[i] = true;
for (int j = 0; j < n; j ++ ) // 枚举该状态的每一位
if (i >> j & 1) { // 当前位为1
if (cnt & 1) st[i] = false; // 如果之前连续的0的个数为奇数,则当前状态不可取
cnt = 0;
}
else cnt ++ ; // 当前位为0
if (cnt & 1) st[i] = false; // 如果当前有奇数个连续的0则状态不可取
}
memset(f, 0, sizeof f);
f[0][0] = 1;
for (int i = 1; i <= m; i ++ ) // 每一列
for (int j = 0; j < 1 << n; j ++ ) // 枚举列的状态
for (int k = 0; k < 1 << n; k ++ ) // 前一列的状态
if ((j & k) == 0 && (st[j | k])) // 如果它们没有冲突,i这一列被占位的情况也是合法的话
f[i][j] += f[i - 1][k]; // 那么这种状态下它的方案数等于之前每种k状态数目的和
cout << f[m][0] << endl; // 求的是第m-1行排满,并且第m-1行不向外伸出块的情况 0~m-1行是题目中可以摆方块的范围
}
return 0;
}
给定一张 n 个点的带权无向图,点从 0∼n−1 标号,求起点 0 到终点 n−1 的最短 Hamilton 路径。
Hamilton 路径的定义是从 0 到 n−1 不重不漏地经过每个点恰好一次。
51
输入格式
第一行输入整数 n。
接下来 n 行每行 n 个整数,其中第 i 行第 j 个整数表示点 i 到 j 的距离(记为 a[i,j])。
对于任意的 x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]≥a[x,z]。
输出格式
输出一个整数,表示最短蒟蒻 Hamilton 路径的长度。
数据范围
1 ≤ n ≤ 20
0 ≤ a[i,j] ≤ 107
输入样例:
5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0
输出样例:
18
首先我们要思考如果让这个NP完全题目复杂度降低,那么可以优先考虑到使用位运算,状态压缩等解决思路。
状态压缩DP分析:
1.本题思路
假设:一共有七个点,用0,1,2,3,4,5,6来表示,那么先假设终点就是5,在这里我们再假设还没有走到5这个点,且走到的终点是4,那么有以下六种情况:
first: 0–>1–>2–>3–>4 距离:21
second: 0–>1–>3–>2–>4 距离:23
third: 0–>2–>1–>3–>4 距离:17
fourth: 0–>2–>3–>1–>4 距离:20
fifth: 0–>3–>1–>2–>4 距离:15
sixth: 0–>3–>2–>1–>4 距离:18
如果此时你是一个商人你会走怎样的路径?显而易见,会走第五种情况对吧?因为每段路程的终点都是4,且每种方案的可供选择的点是0~4,而商人寻求的是走到5这个点的最短距离,而4到5的走法只有一种,所以我们选择第五种方案,可寻找到走到5这个点儿之前,且终点是4的方案的最短距离,此时0 ~ 5的最短距离为(15+4走到5的距离).(假设4–>5=8)
同理:假设还没有走到5这个点儿,且走到的终点是3,那么有一下六种情况:
first: 0–>1–>2–>4–>3 距离:27
second: 0–>1–>4–>2–>3 距离:22
third: 0–>2–>1–>4–>3 距离:19
fourth: 0–>2–>4–>1–>3 距离:24
fifth: 0–>4–>1–>2–>3 距离:26
sixth: 0–>4–>2–>1–>3 距离:17
此时我们可以果断的做出决定:走第六种方案!!!,而此时0~5的最短距离为(17+3走到5的距离)(假设3–>5=5)
在以上两大类情况之后我们可以得出当走到5时:
经过深思熟虑之后,商人决定走以3为终点的最短距离,此时更新最短距离为:22。
当然以此类推还会有以1为终点和以2为终点的情况,此时我们可以进行以上操作不断更新到5这个点的最短距离,最终可以得到走到5这个点儿的最短距离,然后再返回最初的假设,再依次假设1,2,3,4是终点,最后再不断更新,最终可以得出我们想要的答案。
接着思考,我们可以发现,我们所需要的不是整个方案,而只是方案最优解,所以我们只需要记录当前这个方案的最优解即可,那么我们考虑的状态,不就只有,在当前方案 i 中,目前抵达的点是 j。
2.DP分析:
用二进制来表示要走的所以情况的路径,这里用i来代替
例如走0,1,2,4这三个点,则表示为:10111;
走0,2,3这三个点:1101;
状态表示:f[i][j];
集合:所有从0走到j,走过的所有点的情况是i的所有路径
属性:MIN
状态计算:如1中分析一致,0–>·····–>k–>j中k的所有情况
状态转移方程:f[i][j]=min(f[i][j],f[i^(1<
以上转移方程,weight数组为权值 ,也就是weight[k][j]是k点到 j 点的权值
i^(1< 那么这个位运算有什么用处呢,第一点它是在判断第j位的情况,第二点位运算处理速度很快。 笔记学习:#include
作者:灰之魔女
链接:https://www.acwing.com/solution/content/18533/
来源:AcWing
作者:秦淮岸灯火阑珊
链接:https://www.acwing.com/solution/content/789/
来源:AcWing