HDU 4026--Unlock the Cell Phone(状态压缩)

来源: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值