传送门
真是气啊!
这道题一开始我打的暴力,20分的
然后想着优化一下,发现了一个了不得的规律
如果把一个
P
1
P_1
P1确定,
P
i
(
i
>
1
)
P_i(i>1)
Pi(i>1)的取值只能是
P
i
−
l
o
w
b
i
t
(
i
−
1
)
P_{i-lowbit(i-1)}
Pi−lowbit(i−1)
x
o
r
xor
xor
x
(
l
o
w
b
i
t
(
i
−
1
)
<
=
x
<
2
∗
l
o
w
b
i
t
(
i
−
1
)
)
x(lowbit(i-1)<=x<2*lowbit(i-1))
x(lowbit(i−1)<=x<2∗lowbit(i−1))
然后,我就写了优化版暴力。。。发现能过40分,心满意足。。
正解是dp处理一个模拟线段树的东西
然后,以为神仙说他的算法秒了std,一听,WTF!
这不是我的暴力想法吗,他也想到了lowbit这上面
然后我发现只要把我自己的暴力继续优化,做一个小学生都会的改动,把dfs里面加一个记忆化,我就过了!!
就是稍微设计一下状态,需要两维
d
p
[
t
]
[
n
u
m
]
表
示
还
剩
t
个
没
选
,
上
一
个
选
的
是
n
u
m
的
答
案
dp[t][num]表示还剩t个没选,上一个选的是num的答案
dp[t][num]表示还剩t个没选,上一个选的是num的答案
转移就跟dfs一样
40分暴力Code:
#include <bits/stdc++.h>
#define maxn 1010
#define inf 2147483647
using namespace std;
int a[maxn], n, k, ans, w[maxn][maxn];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
int lowbit(int x){ return x & -x; }
void Do(){
int sum = 0;
for (int i = 1; i < n; ++i) sum += w[a[i]][a[i + 1]];
ans = min(ans, sum);
}
void dfs(int k){
if (k > n) Do(); else{
int x = lowbit(k - 1);
for (int i = x; i < (x << 1); ++i){
a[k] = a[k - x] ^ i;
dfs(k + 1);
}
}
}
int main(){
k = read();
n = 1;
for (int i = 1; i <= k; ++i) n <<= 1;
for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) w[i][j] = read();
ans = inf;
for (int i = 0; i < n; ++i){
a[1] = i; dfs(2);
}
printf("%d\n", ans);
return 0;
}
小改动的满分Code:
#include <bits/stdc++.h>
#define maxn 1010
#define inf 2147483647
using namespace std;
int dp[maxn][maxn], n, k, ans, w[maxn][maxn];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
int lowbit(int x){ return x & -x; }
int dfs(int t, int num){
if (t < 1) return 0;
if (dp[t][num] != -1) return dp[t][num];
dp[t][num] = 1e9;
int x = lowbit(t);
for (int i = x; i < (x << 1); ++i)
dp[t][num] = min(dp[t][num], w[num][num ^ i] + dfs(t - 1, num ^ i));
return dp[t][num];
}
int main(){
k = read();
n = 1 << k;
for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j) w[i][j] = read();
memset(dp, -1, sizeof(dp));
printf("%d\n", dfs(n, n));;
return 0;
}

博客讲述一道题的解题过程,起初用暴力法得20分,优化后得40分。正解是用dp处理模拟线段树,后发现将暴力法加记忆化,设计二维状态dp[t][num],转移与dfs相同,即可满分通过。
1609

被折叠的 条评论
为什么被折叠?



