题解:P12236 [蓝桥杯 2023 国 Java A] 连续数组
题解:P12236 [蓝桥杯 2023 国 Java A] 连续数组
思路简述
题意很好理解,不再过多赘述。
看到
理论上来看题解的应该都知道什么是状压 DP,但还是简单介绍一下。
所谓状压 DP,其精髓在于状态压缩,通过将状态转化为二进制的方式进行动态规划,利用二进制中只有
定义
接下来很简单了,遍历所有状态,记录下每种状态中用了几个数,用变量 dp[i|(1<<k-1)][k]+=dp[i][j];。转移之前记得判一下
代码呈现
C++
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=16;
int n,a[N],dp[1<<N][N],ans;
signed main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;i++) cin>>a[i],dp[1<<i-1][i]=1;//输入的同时初始化
dp[1<<n-1][n]=1;
int num=1<<n;
for(int i=0;i<num;i++){
int pos=0,temp=i;
while(temp){
pos+=(temp&1);//计算已用过的数字的数量
temp=temp>>1;
}
for(int j=1;j<=n;j++){
if(!(i>>j-1)&1) continue;//确保j在i中
for(int k=1;k<=n;k++){
if((i>>k-1)&1) continue;//确保k不在i中
if(a[pos]) dp[i|(1<<k-1)][k]+=(k==(j+1)?dp[i][j]:0);//转移方程,注释中的代码作用相同
else dp[i|(1<<k-1)][k]+=(k!=(j+1)?dp[i][j]:0);
/*
if(a[pos]){
if(k==j+1) dp[i|(1<<k-1)][k]+=dp[i][j];
}
else{
if(k!=j+1) dp[i|(1<<k-1)][k]+=dp[i][j];
}
*/
}
}
}
for(int i=1;i<=n;i++) ans+=dp[num-1][i];//累加不同数字作为末尾时的方案数
cout<<ans;
return 0;
}
Java
import java.util.Scanner;
public class Main {
static final int N=16;
static int n,a[]=new int[N];
static long dp[][]=new long[1<<N][N],ans;
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
n=sc.nextInt();
for(int i=1;i<n;i++){a[i]=sc.nextInt();dp[1<<i-1][i]=1;}
dp[1<<n-1][n]=1;
int num=1<<n;
for(int i=0;i<num;i++){
int pos=0,temp=i;
while(temp!=0){pos+=(temp&1);temp>>=1;}
for(int j=1;j<=n;j++){
if(((i>>j-1)&1)==0)continue;
for(int k=1;k<=n;k++){
if(((i>>k-1)&1)!=0)continue;
if(a[pos]!=0)dp[i|(1<<k-1)][k]+=(k==j+1?dp[i][j]:0);
else dp[i|(1<<k-1)][k]+=(k!=j+1?dp[i][j]:0);
}
}
}
for(int i=1;i<=n;i++)ans+=dp[num-1][i];
System.out.println(ans);
}
}