题意: 给定N个点(环状),每次可以销毁连续的三个点, 每个点有一个权值。
问达到全部销毁的状态权值和最小为多少。(每一个状态下,存活的点的权值都要加)
3 ≤ N ≤ 20
think: 显然可以状压。但是貌似也有大佬直接搜出来的。。。人与人之间的差距呀~这个我觉得可能不太好想。。。
首先我们要搜出来的答案是 dp[(1<<(n) - 1]
dp[state]可以由 dp[next_state] + 下一个状态下的伤害值得到。
仔细思考下两个边界 最初和最后。 显然dp[0] = 0;
拿最开始的状态说, 我要销毁掉三个 那么是不要这三个的权值的,
所以dp[(1<<(n) - 1] = dp[下一个状态] + 下一个状态下的权值和。
ps: 老师说有个学姐为了刷DP直接就刷了两百多道。
#include <bits/stdc++.h>
#define ll long long
#define ms(x) memset(x, 0, sizeof(x))
#define inf 0x3f3f3f3f
#define mf(x) memset(x, inf, sizeof(x))
using namespace std;
const int N = 2003;
int a[N], n;
int dp[(1<<20)+5];
int ans;
int dfs(int sta){
if(dp[sta]!=inf){
return dp[sta];
}
for(int i=1;i<=n;i++){
int tmp = sta;
if(sta & (1<<(i-1)) ){
int l, r, damage = 0;
if(i == 1){
l = n;
} else l = i-1;
if(i == n){
r = 1;
} else r = i+1;
tmp -= 1<<(i-1);
if(tmp & (1<<(l-1))){
tmp -= (1<<(l-1));
}
if(tmp & (1<<(r-1)) ){
tmp -= (1<<(r-1));
}
for(int j=1; j<=n; j++){
if(tmp & 1<<(j-1)){
damage += a[j];
}
}
dp[sta] = min(dp[sta], dfs(tmp) + damage);
}
}
return dp[sta];
}
int main()
{
scanf("%d", &n);
for(int i=1;i<=n;i++){
scanf("%d", &a[i]);
}
mf(dp);
dp[0] = 0;
cout<<dfs((1<<n) - 1)<<endl;
return 0;
}