状压dp
状态压缩
即将十进制,字符串,数组等转化为二进制,应用于搜索,动态规划。
P1225 黑白棋游戏
#include<bits/stdc++.h>
using namespace std;
void write(int k){
if(k/10==0){
putchar((char)(k+'0'));
return;
}
write(k/10);
putchar((char)(k%10+'0'));
return;
}
const int N=1e5+10;
int S,T,last[N];
bool visited[N];
queue<int>q;
void scan(int &k){
for(int i=1;i<=16;i++){
char ch;cin>>ch;
k=(k<<1)+ch-'0';
}
return;
}
void solve(){
q.push(S);
visited[S]=1;last[S]=-1;
while(!q.empty()){
int k=q.front(),z;q.pop();
for(int i=2;i<=16;i++){
if(i%4!=1&&((k>>(i-1))&1)!=((k>>(i-2))&1)){
z=k^(3<<(i-2));
if(visited[z]) continue;
visited[z]=1;
last[z]=k;
if(z==T) return;
q.push(z);
}
if(i>=5&&((k>>(i-1))&1)!=((k>>(i-5))&1)){
z=k^(1<<(i-1))^(1<<(i-5));
if(visited[z]) continue;
visited[z]=1;
last[z]=k;
if(z==T) return;
q.push(z);
}
}
}
return;
}
void print(int k,int cnt){
if(k==S){
write(cnt);
putchar('\n');
return;
}
print(last[k],cnt+1);
int z=last[k]^k,num[2];
bool f=0;
for(int i=0;i<16;i++)
if(z&(1<<i))
num[f]=i+1,f^=1;
num[0]=16-num[0]+1;num[1]=16-num[1]+1;
write((num[0]-1)/4+1);
write((num[0]-1)%4+1);
write((num[1]-1)/4+1);
write((num[1]-1)%4+1);
putchar('\n');
return;
}
signed main(){
scan(S);scan(T);
if(S==T){
cout<<0<<'\n';
return 0;
}
solve();
print(T,0);
return 0;
}
一些基本操作
· 取s的第k位:(s>>(k-1))&1
. 将s的第k位取反:s^=1<<(k-1)
. 枚举s的子集:for(int t=s;t;t=s&(t-1)){...}
· lowbit技术:int lowbit(int k){return k&-k}
P5911 [POI 2004] PRZ
#include<bits/stdc++.h>
using namespace std;
const int INF=1e5+10;
int W,n,t[20],w[20],dp[INF];
signed main(){
ios::sync_with_stdio(0);
cin>>W>>n;
for(int i=1;i<=n;i++)
cin>>t[i]>>w[i];
for(int i=1;i<=(1<<n)-1;i++){
int sum=0;
for(int j=1;j<=n;j++)
if((i>>(j-1))&1)
sum+=w[j],dp[i]=max(dp[i],t[j]);
if(sum>W) dp[i]=INF;
for(int s=i&(i-1);s;s=i&(s-1))
dp[i]=min(dp[i],dp[s]+dp[i-s]);
}
cout<<dp[(1<<n)-1]<<'\n';
return 0;
}
时间复杂度为O(n^3),浅浅证明一下。
对于每个包含k个1的s枚举子集时,枚举次数为:2^k
而含k个1的集合的个数为:C(n,k),运行次数为:2^k*C(n,k)
故此代码的总运行次数为:
2^0 *C(n,0) +2^1 *C(n,1) +2^2 *C(n,2) +...+2^(n-1) *C(n,n-1) +2^n C(n,n)
=C(n,0)2^0 1^n+ C(n,1)2^1 1^n+ C(n,2)2^2 1^n +... +C(n,n-1)2^(n-1) 1^n+ C(n,n)2^n *1^n
=(1+2)^n
=3^n
得证。
P2831 [NOIP 2016 提高组] 愤怒的小鸟
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-6;
int T,n,m,dp[(1<<18)+10],min0[(1<<18)+10],line[20][20];
double x[20],y[20];
void Get_ab(double &a,double &b,int _1,int _2){
if(x[_1]==x[_2]) return;
a=(y[_1]*x[_2]-y[_2]*x[_1])/(x[_1]*x[_1]*x[_2]-x[_2]*x[_2]*x[_1]);
b=(y[_1]*x[_2]*x[_2]-y[_2]*x[_1]*x[_1])/(x[_1]*x[_2]*x[_2]-x[_1]*x[_1]*x[_2]);
return;
}
bool check(double a,double b,int k){
return abs(a*x[k]*x[k]+b*x[k]-y[k])<eps;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
for(int i=0;i<(1<<18)+10;i++){
int k=1;
while(i&(1<<(k-1))) k++;
min0[i]=k;
}
cin>>T;
while(T--){
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j){
line[i][j]=1<<(i-1);
continue;
}
line[i][j]=0;
double a=0,b;
Get_ab(a,b,i,j);
if(a>-eps) continue;
for(int k=1;k<=n;k++)
if(check(a,b,k))
line[i][j]|=1<<(k-1);
}
for(int s=1;s<=(1<<n)-1;s++)
dp[s]=100;
for(int s=0;s<=(1<<n)-1;s++)
for(int i=min0[s];i<=n;i++)
dp[s|line[min0[s]][i]]=min(dp[s|line[min0[s]][i]],dp[s]+1);
cout<<dp[(1<<n)-1]<<'\n';
}
return 0;
}
时间复杂度O(T * n * 2^n)
主要在于dp的推导,细节在于一些实数误差。

浙公网安备 33010602011771号