题意: 给定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; }
扫描二维码关注公众号,回复:
1684023 查看本文章