现在的位置: 首页 > 综合 > 正文

上海区域赛Unlock the Cell Phone

2018年11月09日 ⁄ 综合 ⁄ 共 2105字 ⁄ 字号 评论关闭
/* 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4026
 题目来源:上海区域赛Unlock the Cell Phone
报告人:SpringWater
题目类型:状态压缩
题目大意为:在一个存在跨越点和坏死点和正常点的矩阵中,求出遍历所有正常点且仅一次的哈密顿通路的数量总数 思路:这跟哈密顿图的解法差不多,其实就是求哈密顿通路个数吧。DP[i][s], 表示以结点i为最后一个连接点路径状态为s时的图像个数。转移式为DP[i][s] = DP[1][s ^ (1 << i)] + DP[2][s ^ (1 << i)] + ...DP[n][s ^ (1 << i)]。最后所有的DP[k][(1 << n + 1) - 1]求和为解。

 解题感悟:此题是一个状态压缩类型的题,这类题型一般属于较难题,因为他不仅设计到图论的知识,还涵盖了数 论和数学建模的理论,以前从没做过类似的题目,所以这题的解题思路基本都是借鉴了别人的解题报告, 本想将他 处理没状态可达的方法优化一下的,即:我不罗列所有中间序号的点,而是从正常点向八个方向扩展, 但是后来发现虽然时间复杂度虽然有改观,但是编程复杂度 就大大加强了,所以 除了判断可达那里我做了 一些空间上的优化以外 其他的都没怎么改动,因为感觉别人的代码确实很精致美妙,!

 通过这初次对状态压缩,感觉它是一个非常实用的思想,并且是很灵活,比起纯的dp要难与理解很多,因为关 键它涉及了很多二进制数论的运用!使得学起来比较吃力,但是我相信通过长时间多的磨练我会把它拿下的!目 前接算是对二维的状态压缩给解决了,接下来就深入到三维的状态压缩,尽管可能会有点难! */

#include<stdio.h>
#include<string.h>
int G[30],ordinary[17];
int SA[17][17],Hash[30];
__int64dp[17][1<<17];
int n,m,label;
int inputdata()//录入数据
{
    inti,j=n*m;
    for(i=0;i<j;i++)
    {
            scanf("%d",&G[i]);
            if(!G[i])
            {
                Hash[i]=label;     ///用于快速找出当前被接入点的状态位置
                ordinary[label++]=i;///用于后面将两个点中间的所有点罗列出来
            }
        }
        return0;
}
int online(int x1,inty1,int x2,int y2,intmidx,int midy)
{
    return(y1-midy)*(x2-midx)==(y2-midy)*(x1-midx);    //判断三点是否构成一条直线
}
int prepare()
{
    int i,j,k,x1,y1,x2,y2,midx,midy;
    memset(SA,0,sizeof(SA));
    for(i=0;i<label-1;i++)
        for(j=i+1;j<label;j++)
        {
            x1=ordinary[i]%m;
            y1=ordinary[i]/m;
              
            x2=ordinary[j]%m;
            y2=ordinary[j]/m;
  
            for(k=ordinary[i]+1;k<ordinary[j];k++)
            {
                midx=k%m;
                midy=k/m;
                if(G[k]==1||!G[k])
                {
                    if(online(x1,y1,x2,y2,midx,midy))
                    {
                        if(G[k]==1)//被坏死点阻隔
                        {
                            SA[i][j]=SA[j][i]=-1;
                            break;
                        }
                        if((G[k]==0))//将所有的中间正常点记录下来
                        {
                            SA[i][j] |= (1<<Hash[k]);
                            SA[j][i] |= (1<<Hash[k]);
                        }
                    }
                }
            }
        }
        return0;
}
int canreach(int i,int j,ints)//判断i,j点在当前s状态能否可达
{
    if(SA[i][j]==-1)
        return0;
    if(!SA[i][j])
        return1;
    returnSA[i][j]==(SA[i][j]&s);
}
int work()
{
    intsum=1<<label;
    inti,j,k;
    for(i=1;i<sum;i++)
    {
        for(j=0;j<label;j++)
        {
            if(i==1<<j)//当前状态是第一次接入点
            {
                dp[j][i]=1;
                continue;
            }
            else    
                dp[j][i]=0;
            if(i&(1<<j))
            {
                for(k=0;k<label;k++)//罗列出当前点j可能接入的所有入口点k
                {
                    if((k!=j)&&(i&(1<<k))&&canreach(j,k,i))
                        dp[j][i]+=dp[k][i-(1<<j)];
                }
            }
        }
    }
    return0;
}
int main()
{
    intsum,i;
    __int64ans;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        label=0;
        inputdata();
        prepare();
        work();
        ans=0;
        sum=(1<<label)-1;
        for(i=0;i<label;i++)//将所有接入点的最后都接入的状态累加起来
            ans+=dp[i][sum];
        printf("%I64dn",ans);
    }
    return0;
}

抱歉!评论已关闭.