题解/算法 {5729. 闯关游戏}
@LINK: https://www.acwing.com/problem/content/description/5732/
;
很容易想到是哈密顿路径DP, 看一个错误代码
int N, M, K;
cin>> N>> M>> K;
vector<int> A(N); for( auto & i : A){ cin>> i;}
int Edge[ 20][ 20];
std::memset( Edge, 0, sizeof(Edge));
FOR_( _, 1, K){
int a, b, v; cin>>a>>b>>v; --a, --b;
Edge[ a][ b] = v;
}
static Int64_ DP[ 1<<18][ 18];
Int64_ ANS = 0;
FOR_( i, 0, N-1){
DP[1<<i][i] = A[i];
if( 1 == M){
ANS = max( ANS, (Int64_)A[i]);
}
}
FOR_( X, 3, (1<<N)-1){
for( auto tx = X; tx > 0; tx ^= (1<< ___Binary_LowBit_1(tx))){
auto Y = ___Binary_LowBit_1(tx);
auto & curDP = DP[X][Y];
curDP = 0;
int preX = (X ^ (1<<Y));
for( auto ttx = preX; ttx > 0; ttx ^= (1<< ___Binary_LowBit_1(ttx))){
auto preY = ___Binary_LowBit_1(ttx);
curDP = std::max( curDP, DP[preX][preY] + Edge[preY][Y] + A[Y]);
}
if( __builtin_popcount( X) == M){
ANS = max( ANS, curDP);
}
}
}
cout<< ANS;
这个代码的错误在于, 你枚举状态X 是for( X, 3, ...)
你的意图是 只让X
遍历那些二进制至少有两个1
比特的状态 因为我们提前将X = 1<<{0,1,2,3,...}
的这些状态给赋值了, 可是… 可是… 你还是会遍历的 那些二进制里只有一个1
的那些状态的呀 比如4, 8, 16, ...
, 于是就导致 你将curDP = 0
即本来你赋值了DP[ 1<<?][?] = A[?]
而你现在又清空了 即DP[1<<?][?] = 0
这当然就错误了;
因此, 你写DP时 要养成好的习惯, 就是你一个FOR
循环 直接遍历所有状态, 不要分来开 (你上面的代码 就是两个for
循环 给分开了) 然后对于特殊清空 就if
特判掉, 这就很规范 代码规范很重要呀
static Int64_ DP[ 1<<18][ 18];
Int64_ ANS = 0;
FOR_( X, 1, (1<<N)-1){
if( __builtin_popcount( X) == 1){
auto Y = ___Binary_LowBit_1(X);
DP[ X][ Y] = A[Y];
if( __builtin_popcount( X) == 1){
ANS = max( ANS, DP[ X][ Y]);
}
}
else{
for( auto tx = X; tx > 0; tx ^= (1<< ___Binary_LowBit_1(tx))){
auto Y = ___Binary_LowBit_1(tx);
auto & curDP = DP[X][Y];
curDP = 0;
int preX = (X ^ (1<<Y));
for( auto ttx = preX; ttx > 0; ttx ^= (1<< ___Binary_LowBit_1(ttx))){
auto preY = ___Binary_LowBit_1(ttx);
curDP = std::max( curDP, DP[preX][preY] + Edge[preY][Y] + 0LL + A[Y]);
}
if( __builtin_popcount( X) == M){
ANS = max( ANS, curDP);
}
}
}
}
cout<< ANS;