初步---状压DP
【使用情况】:
在状态比较多的情况下,同时状态只需要记录是或非,使用二进制将其压缩,从而达到缩减时间复杂度的效果。
由于要使用二进制来表示状态,所以这类问题的数据范围会相对较小\(\left ( n\leq 20左右\right )\),时间复杂度经常含有 O(2n).代替爆搜的常用方法
【二进制基础】:
若当前状态为s,对s有如下操作:(第i位是指从右往左数的第i位)
1.判断第i位是否为0:(s&(1<<(i-1)))==0
2.判断第i位是否为1:((1<<(i-1))&s)>0
3.将第i位置0:s&(~(1<<(i-1)))
4.将第i位置1:s|(1<<(i-1))
5.把一个数字二进制下最靠右的第一个1去掉:s=s&(s-1)
6.最右边 i 位:s&((1<<(i-1))-1)
7.第i位取反:s^(1<<(i-1))
8.枚举子集:
1 for(int s1=s;s1!=0;s1=(s1-1)&s){ 2 s2=s^s1; 3 }
入门题目:
题解:其实可以比较容易想到dp,dp部分状态转移见代码,状态情况从\(000000000000000\left ( 15位\right )\sim111111111111111\left ( 15位\right )\)\(\left ( 2进制形式\right )\),化为\(10\)进制,最大也就是\(2^{16}-1\),可以开一个二维dp数组:\(dp\left [ i\right ]\left [ j\right ]表示i状态的最后一步是j位置\)
具体见代码:
1 #include <iostream> 2 #include <bits/stdc++.h> 3 using namespace std; 4 typedef long long ll; 5 const int maxn=1e5+10; 6 const double inf=9999999999.9; 7 const int mod=1e9+7; 8 #define rep(i,first,second) for(int i=first;i<=second;i++) 9 #define dep(i,first,second) for(int i=first;i>=second;i--) 10 11 struct point{double x,y;}p[30]; 12 double dp[1<<15][20]; 13 double dis[20][20]; 14 int n; 15 16 double Dis(int i,int j){ 17 double xx=p[i].x-p[j].x; 18 double yy=p[i].y-p[j].y; 19 double d=sqrt((1.0*xx*xx)+(1.0*yy*yy)); 20 return d; 21 } 22 23 void dpFuc(){ 24 rep(i,1,(1<<n)-2){//计算从第i种状态可以到达的所有状态 25 for(int pre=0;pre<n;pre++){//最后一步,从第pre位 26 if( !(i&(1<<pre))) continue;//跳过还没走过的位置 27 for(int now=0;now<n;now++){//到now位 28 if((i&(1<<now))) continue;//跳过已经走过的位置 29 dp[i|(1<<now)][now]=min(dp[i|(1<<now)][now],dp[i][pre]+dis[now+1][pre+1]); 30 } 31 } 32 } 33 } 34 35 int main() 36 { 37 ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 38 memset(dp,127,sizeof(dp));//double 型赋最大值 39 40 cin>>n; 41 p[0].x=0.00,p[0].y=0.00; 42 rep(i,1,n) cin>>p[i].x>>p[i].y; 43 rep(i,0,n){//用(0,0)到其余各点的距离初始化dp数组 44 rep(j,i+1,n){ 45 double dist=Dis(i,j); 46 dis[i][j]=dist;dis[j][i]=dist; 47 } 48 } 49 rep(i,0,n-1){ 50 dp[1<<i][i]=dis[0][i+1]; 51 } 52 53 dpFuc(); 54 double ans=inf; 55 rep(i,0,n-1){ 56 ans=min(ans,dp[(1<<n)-1][i]); 57 } 58 cout<<fixed<<setprecision(2)<<ans<<endl; 59 return 0; 60 }
注意:

1 #include <iostream> 2 #include <bits/stdc++.h> 3 using namespace std; 4 typedef long long ll; 5 const int maxn=1e5+10; 6 const double inf=9999999999.9; 7 const int mod=1e9+7; 8 #define rep(i,first,second) for(int i=first;i<=second;i++) 9 #define dep(i,first,second) for(int i=first;i>=second;i--) 10 11 struct point{double x,y;}p[30]; 12 double dp[1<<15][20]; 13 double dis[20][20]; 14 int n; 15 16 double Dis(int i,int j){ 17 int xx=p[i].x-p[j].x;//这里错了,一定要定义成double 18 int yy=p[i].y-p[j].y;//这里错了,一定要定义成double 19 double d=sqrt((1.0*xx*xx)+(1.0*yy*yy)); 20 return d; 21 } 22 23 void dpFuc(){ 24 rep(i,1,(1<<n)-2){ 25 for(int pre=0;pre<n;pre++){ 26 if( !(i&(1<<pre))) continue; 27 for(int now=0;now<n;now++){ 28 if((i&(1<<now))) continue; 29 dp[i|(1<<now)][now]=min(dp[i|(1<<now)][now],dp[i][pre]+dis[now+1][pre+1]); 30 } 31 } 32 } 33 } 34 35 int main() 36 { 37 ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 38 memset(dp,127,sizeof(dp));//double 型赋最大值 39 40 cin>>n; 41 p[0].x=0.00,p[0].y=0.00; 42 rep(i,1,n) cin>>p[i].x>>p[i].y; 43 rep(i,0,n){//用(0,0)到其余各点的距离初始化dp数组 44 rep(j,i+1,n){ 45 double dist=Dis(i,j); 46 dis[i][j]=dist;dis[j][i]=dist; 47 } 48 } 49 rep(i,0,n-1){ 50 dp[1<<i][i]=dis[0][i+1]; 51 } 52 53 dpFuc(); 54 double ans=inf; 55 rep(i,0,n-1){ 56 ans=min(ans,dp[(1<<n)-1][i]); 57 } 58 cout<<fixed<<setprecision(2)<<ans<<endl; 59 return 0; 60 }