状态压缩,就是把一个整数用二进制表示
//1.蒙得里安的梦想
//核心:当横行摆完了,整个图就确定了
// 状态表示f[i][j]:第i列有j(二进制表示)方案从上一格伸出
// 状态计算f[i][j]+=f[i-1][k],(j&k=0&&j|k不存在奇数个零)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=12,M=1<<N;
int n,m;
bool st[M];
int f[N][M];
int main()
{
int n,m;
while(cin>>n>>m,n||m)
{
memset(f,0,sizeof f);//初始化结果
//计算重复0,j|k有连续零为false,用于后续判断是否有奇数零
for(int i=0;i<1<<n;i++)
{
st[i]=true;
int cnt=0;//标记零的个数
for(int j=0;j<n;j++)
if(i>>j&1)
{
if(cnt&1)st[i]=false;//有奇数个0,不合法
cnt=0;
}
else cnt++;
if(cnt&1)st[i]=false;//判断最后
}
f[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=0;j<1<<n;j++)
for(int k=0;k<1<<n;k++)
if((j&k)==0&&st[j|k])
f[i][j]+=f[i-1][k];
cout<<f[m][0]<<endl;
}
return 0;
}
//2.最短Hamilton路径
//给出一个 有n个点的无向图,从零到n-1不重不漏恰好经过每个点一次
// 状态表示f[i][j]:所有从0到j,且走过的所有点是i;
// i=(111001)表示除了第二,三点没走过,其他点走过了;属性:min
// 如何转移:用倒数第二个点是啥(k)来分类
// 状态计算 :f[i][j]=f[i-{j}][k]+a[k][j];(k=0,1...n-1)
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=20,M=100000;
//const int N=20,M=1<<N;不知道为什么爆掉了
int n;
int w[N][N];
int f[M][N];
int main()
{
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>w[i][j];
memset(f,0x3f,sizeof f);
f[1][0]=0;
for(int i=0;i<1<<n;i++)//标识走过了哪些点
for(int j=0;j<n;j++)//表示终点(局部或最终)
if(i>>j&1)//第j位走过了
for(int k=0;k<n;k++)//k表示走到j这个点之前,以k为终点的最短距离
if((i-(1<<j))>>k&1)
f[i][j]=min(f[i][j],f[i-(1<<j)][k]+w[k][j]);//更新最短距离
cout<<f[(1<<n)-1][n-1]<<endl;
return 0;
}