HDU 4026 Unlock the Cell Phone 状压dp(类似TSP)
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4026
Unlock the Cell Phone
Memory Limit: 65768/65768 K (Java/Others)
输入
The input contains several test cases. Each test case begins with a line containing two integers n and m (1 <= n, m <= 5), indicating the row and column number of the lock keypad. The following n lines each contains m integers kij indicating the properties of each key, kij=0 stands for an ordinary key, kih=1 stands for a forbidden key; and kij=2 stands for an inactive key. The number of ordinary keys is greater than zero and no more than 16.
输出
For each test, output an integer indicating the number of different lock patterns.
样例输入
2 2
0 0
0 0
3 3
0 0 0
0 2 1
0 0 0
样例输出
24
2140
题意
给你一个手势解锁的n*m的键盘,0表示可以正常使用的,1表示不能碰触并且不能越过的,2表示不能碰触但是可以越过的。 问要把所有正常的都激活一遍的总的方案数有多少种。
题解
处理下什么情况是非法的,然后把可激活的单独拿出来激活下。
代码
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf
typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;
const int INF=0x3f3f3f3f;
const LL INFL=10000000000000000LL;
const double eps=1e-9;
const double PI = acos(-1.0);
//start----------------------------------------------------------------------
const int maxm=17;
int arr[11][11];
int mp[33][33];
int mmp[33];
int n,m;
VI G[33][33];
LL dp[1<<maxm][maxm];
///判断中间有没有经过还没访问过的点或者中间有没有静止越过的点
bool ok(int sta,int s1,int s2){
if(mp[s1][s2]==1) return false;
rep(i,0,G[s1][s2].sz()){
int x=G[s1][s2][i];
int id=mmp[x];
if(!(sta&(1<<id))) return false;
}
return true;
}
void init(){
clr(mp,0);
clr(mmp,-1);
rep(i,0,33) rep(j,0,33) G[i][j].clear();
}
int main() {
while(scf("%d%d",&n,&m)==2&&n){
init();
///把等于0的点单独挖出来状压
VI vec;
rep(i,0,n) rep(j,0,m){
scf("%d",&arr[i][j]);
if(arr[i][j]==0){
vec.pb(i*m+j);
}
}
rep(i,0,vec.sz()){
mmp[vec[i]]=i;
}
///处理线段中间的点
rep(i,0,vec.sz()){
rep(j,0,vec.sz()){
if(i==j) continue;
int xi=vec[i]/m,yi=vec[i]%m;
int xj=vec[j]/m,yj=vec[j]%m;
for(int x=min(xi,xj);x<=max(xi,xj);x++){
for(int y=min(yi,yj);y<=max(yi,yj);y++){
if(x==xi&&y==yi||x==xj&&y==yj) continue;
if((yi-y)*(xj-x)!=(yj-y)*(xi-x)) continue;
if(arr[x][y]==1){
mp[vec[i]][vec[j]]=1;
}
if(mp[vec[i]][vec[j]]==1) continue;
if(arr[x][y]==0){
G[vec[i]][vec[j]].pb(x*m+y);
}
}
}
}
}
///状压
int tot=vec.sz();
clr(dp,0);
rep(i,0,tot){
dp[1<<i][i]=1;
}
rep(i,0,(1<<tot)){
rep(j,0,tot){
if(!(i&(1<<j))) continue;
rep(k,0,tot){
if(k==j||(i&(1<<k))==0) continue;
if(ok(i,vec[k],vec[j])){
dp[i][j]+=dp[i^(1<<j)][k];
}
}
}
}
LL ans=0;
rep(i,0,tot){
ans+=dp[(1<<tot)-1][i];
}
prf("%lld\n",ans);
}
return 0;
}
//end-----------------------------------------------------------------------