来源:http://acm.hdu.edu.cn/showproblem.php?pid=4026
题意:输入n,m表示图案为n*m的规模(n,m<=5),然后输入一个n*m的矩阵g,g[i][j]表示该点的类型,为0时表示普通的点能触点能滑动,但不能跨,为1时这个点不能被点击和滑过,为2时表示这点能跨过但不能触点。求连接所有的普通点能构成多少个图。
分析:状态压缩dp,dp[i][k],表示以结点i为最后一个连接点路径状态为k时的图像个数。转移式为dp[i][k] = dp[1][k ^ (1 << i)] + dp[2][k ^ (1 << i)] + ...dp[n][k ^ (1 << i)]。最后所有的dp[i][(1 << n + 1) - 1]求和为解。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <cmath>
#include <stack>
#include <set>
#include <vector>
#include <climits>
using namespace std;
const int MAX = 30;
int n, m;
int data[MAX];
int hqs[MAX];
int hqn[MAX];
int d[MAX][MAX];
int s[MAX][MAX];
long long dp[17][1 << 17];
bool isline(int x1,int y1,int x2,int y2,int x3,int y3)
{
return (y2-y1)*(x3-x1)==(y3-y1)*(x2-x1);
}
void build(int e)
{
memset(d,0,sizeof(d));
memset(s,0,sizeof(s));
for(int i=0;i<e;i++)
{
for(int j =i+1;j<e;j++)
{
int start=hqs[j];
int end=hqs[i];
if(start>end)
start^=end^=start^=end;
int xf=start/m;
int yf=start%m;
int xt=end/m;
int yt=end%m;
for(int k=start+1;k<end;k++)
{
int xk=k/m;
int yk=k%m;
if(isline(xf,yf,xt,yt,xk,yk))
{
if(data[k] == 1)
{
d[i][j]=d[j][i]=-1;
break;
}
if(data[k]==0)
{
s[i][j]|=(1<<hqn[k]);
s[j][i]|=(1<<hqn[k]);
}
}
}
}
}
}
bool ok(int i,int j,int k)
{
if(d[i][j] == -1)
return false;
if((k&s[i][j])!= s[i][j])
return false;
return true;
}
int main()
{
// freopen("in.txt","r",stdin);
while(cin>>n>>m&&n&&m)
{
int e=0;
int num = n * m;
for(int i=0;i<num;i++)
{
scanf("%d",&data[i]);
if(data[i]==0)
{
hqs[e]=i;
hqn[i]=e++;
}
}
build(e);
int end=1<<e;
for(int k=1;k<end;k++)
{
for(int i=0;i<e;i++)
{
if(k==(1<<i))
dp[i][k]=1;
else
dp[i][k]=0;
if((k&(1<<i))>0)
{
for(int j=0;j<e;j++)
{
if(j!=i&&(k&(1<<j))>0)
{
if(ok(i, j, k))
{
dp[i][k]+=dp[j][k-(1<<i)];
}
}
}
}
}
}
long long ans = 0;
for(int i=0;i<e;i++)
{
ans+=dp[i][end-1];
}
printf("%I64d\n",ans);
}
return 0;
}