算法总结—区间 DP3+状压 DP1
环形区间 DP
环形区间 DP 断环成链就可以了,答案就可以从 \([1,n],[2,n+1],[3,n+2],\cdots,[n,2n-1]\) 这些区间的答案。
[NOI1995] 石子合并
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[205];
int sum[205];
int dp[205][205];
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
a[i+n]=a[i];
}
for(int i=1;i<=n+n;i++){
sum[i]=sum[i-1]+a[i];
for(int j=1;j<=n+n;j++){
dp[i][j]=1e18;
}
dp[i][i]=0;
}
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n+n;i++){
int j=i+len-1;
for(int k=i;k<j;k++){
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
}
}
}
int Min=1e18;
for(int i=1;i<=n;i++){
Min=min(Min,dp[i][i+n-1]);
}
cout<<Min<<"\n";
for(int i=1;i<=n+n;i++){
sum[i]=sum[i-1]+a[i];
for(int j=1;j<=n+n;j++){
dp[i][j]=0;
}
}
for(int len=2;len<=n;len++){
for(int i=1;i+len-1<=n+n;i++){
int j=i+len-1;
for(int k=i;k<j;k++){
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
}
}
}
int Max=-1e18;
for(int i=1;i<=n;i++){
Max=max(Max,dp[i][i+n-1]);
}
cout<<Max;
return 0;
}
状压 DP
海贼王之伟大航路
这道题的 \(n\leq 16\),可以尝试用 DFS,但时间复杂度是 \(\mathcal{O}(n!)\) 的。在 DFS 中有许多重复的地方,比如说走过一个点集,且最后停留在一个点,这个情况会被 DFS 好几遍,所以可以把这个答案记下来。
状态
\(dp_{i,j}\) 表示所选集合为 \(i\),最后停留在点 \(j\) 最小的答案(这 \(n\) 个点是 \(0\) 到 \(n-1\))。
答案
\(dp_{2^n-1,n-1}\)。
状态转移方程
\(dp_{i,j}=\min\{dp_{i\oplus 2^j,k}+dis_{k,j}\}\),其中 \(i\) 在二进制上的第 \(j\) 和\(k\) 为 \(1\)。
初始值
\(dp_{i,j}=\inf(0\leq i<2^n,0\leq j<n),dp_{1,0}=0\)
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[20][20];
int dp[65550][20];
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
cin>>a[i][j];
}
}
int N=1<<n;
memset(dp,0x3f,sizeof dp);
dp[1][0]=0;
for(int i=0;i<N;i++){
for(int j=0;j<n;j++){
if((i>>j)&1){
for(int k=0;k<n;k++){
if((i>>k)&1){
dp[i][j]=min(dp[i][j],dp[i^(1<<j)][k]+a[k][j]);
}
}
}
}
}
cout<<dp[N-1][n-1];
return 0;
}

浙公网安备 33010602011771号