蒙德里安的梦想
当横向的小方格放好后,纵向小方格的位置是确定的,即总共数量等于横着摆放的小方格的方式的数量
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=12,M=1<<N;
long long dp[N][M];
//dp[i][j]表示第i列总方案数
//j表示第i列的状态,即第i-1列放入的小方块在第i列占用了哪些行,是一串二进制数,eg.01101
bool st[N];
//表示第i列的状态是否合法,当出现以下两种状态时不合法
//1.当第i列有连续奇数个0
//2.出现重叠即当i-1列状态为k时,k&j!=0
int main(){
int n,m;
while(cin>>n>>m,n||m){
memset(dp,0,sizeof dp);//初始化
//所有状态
for(int i=0;i<1<<n;i++){
st[i]=true;//假设当前列状态成立;
int cnt=0;//表示当前状态连续0的个数
//状态的每一位
for(int j=0;j<n;j++){
if(i>>j&1){//如果当前状态位是1,判断此时是否有奇数个0
if(cnt&1)st[i]=false;//第i个状态中存在奇数个0,是不合法的
cnt=0;
}else{
cnt++;
}
}
if(cnt&1)st[i]=false;//判断最后一段中是否存在奇数个0
}
//dp过程
//列数从0开始,第0列肯定不会被上一列占用位置,只能竖着放,dp[0][0]=1,只有一种方案数;
dp[0][0]=1;
//遍历每一列
for(int i=1;i<=m;i++){
//枚举所有状态
for(int j=0;j<1<<n;j++){
//枚举第i-1列所有状态
for(int k=0;k<1<<n;k++){
if((j&k)==0&&st[j|k]){
dp[i][j]+=dp[i-1][k];
}
}
}
}
//第m-1列没有占用第m列时,为最终答案
printf("%lld\n",dp[m][0]);
}
return 0;
}
最短Hamilton路径(旅行商问题)
旅行商问题是一个NP完全问题,没办法在一个多项式时间求出来
Hamilton路径:从0点到n-1点,不重不漏的经过每个点的路径
暴力做法:时间复杂度O((n-1)!),不得行
只需要考虑两个因素:
1.哪些点已经被用过(最右为0) 最多20个点 220
2.目前停在了哪个点上 20
总共的状态:220*20=2 *107个
1.状态表示 dp[i][j]
i.集合 表示从0走到j,走过的点是i的所有路径
ii.属性 Min
1.状态计算
按倒数第二个点为k分
dp [state] [ j ]
state:
表示当前哪个点已被用过,用一个20位的二进制数表示,位i为1时表示该位存在(状态压缩)j:
表示当前停在了哪个点上状态转移方程,假如从k转移过来
dp[state][j]=dp[state_k][k]+w[k][j]
(state_k = state去掉j之后的集合,state_k里要包含k)
时间复杂度=状态数量*状态转移时间复杂度
2 * 107*20=4 * 108
c++小贴士:
堆空间 静态变量,全局变量
栈空间 所有开在函数内部的空间(默认大小4M)
所以一般把大数组开到全局变量里
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=20,M=1<<20;
int w[N][N];
int dp[M][N];
int main(){
int n;
scanf("%d",&n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&w[i][j]);
}
}
memset(dp,0x3f,sizeof dp);
dp[1][0]=0;
//在0点,状态机0位为1,当前位置为0,表示当前还没有走任何路程
//遍历每一个状态
for(int i=0;i<=1<<n;i++){
for(int j=0;j<n;j++){
//判断当前状态是否合法,即第j位是否为1
if(i>>j&1){
//枚举每个未走过的点
for(int k=0;k<n;k++){
//判断state_k(即state-j)的第k位是否为1,减法优先级比右移运算大
if(i-(1<<j)>>k&1){
dp[i][j]=min(dp[i][j],dp[i-(1<<j)][k]+w[k][j]);
}
}
}
}
}
printf("%d",dp[(1<<n)-1][n-1]);
return 0;
}