我是靠谱客的博主 眼睛大灯泡,最近开发中收集的这篇文章主要介绍[caioj]1491: 基于连通性状态压缩的 动态规划问题:Tony's Tour..我的做法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

【题目描述】
给你一个 n*m 的地图,有的格子存在障碍,求 从左下角走到右下角 且经过所有非障碍格子一次(仅一次)的路径总数。
【输入】
多组数据,每组数据开头两个整数 n,m(0< n,m<=8),接下来描述一个 n*m 的地图,“#”表示该格子有障碍,“.”表示该格子无障碍。当 n = 0 ,m = 0时,输入结束。
【输出】
对于每组数据,输出符合条件的路径条数。
【样例输入】
2 2
..
..
2 3

..


3 4
….
….
….
0 0
【样例输出】
1
1
4
来源
Poj 1739

我的做法

一开始我想只要到了(n,m),只要这个点只有一个插头,就累加。。
那有一个难点,就是怎么保证开始点在左下角
那么我们来想啊
左下角的话,当扩展到他的时候,他要么有一个上插头,要么没有
当他有上插头的时候,下一次就少一个左插头
否则就给一个左插头
【说实话,这个讨论还写了挺久】
写成代码大概长这样

if (u==n&&i==1)//是起点 
{
if (p==0&&q==0)
{
change(st,i+1,1);
ADD;
}
else
{
change(st,i+1,0);
ADD;
}
continue;
}

然后终点判断也比较简单

if (u==n&&i==m)//终点
{
if (p>0&&q>0) continue;//不可以有两个插头
if (p==0&&q==0) continue;//不可以没有插头 
ans=ans+sum;
continue;
}

然后就可以了。。

#include<cstdio>
#include<cstring>
#define ADD add(st,sum)
typedef long long LL;
const int MOD=100037;
const int N=15;
bool map[N][N];//是不是可以走 
int n,m;
int xx=-1,yy;//我们要寻找一个最下
最右的点——>当到他的时候就可以累计答案了
LL ans=0;//答案个数
LL a[2][MOD];//这种状态的数目,其实差不多是来辅助下面的数组 
int b[2][MOD];//状态
int num[2];//状态个数
int HASH[MOD];
/*0:无插头状态
1:左括号插头
2:右括号插头*/
int get (int x,int p)//第j位
当然是四进制里面的第j位啦 
{return (x>>(p-1)*2)&3;}
int now;
void add (int st,LL sum)//给当前的决策状态加上这个状态
{
int ss=st%MOD;
while (HASH[ss]!=-1&&b[now][HASH[ss]]!=st)
{
ss++;ss%=MOD;
if (ss==0) ss=1;
}
if (HASH[ss]==-1)
{
HASH[ss]=++num[now];
b[now][num[now]]=st;
a[now][num[now]]=sum;
}
else a[now][HASH[ss]]+=sum;
}
void change (int &s,int p,int v)//把s的第p位改为v
{
s=s^(get(s,p)<<(p-1)*2);
s=s^(v<<(p-1)*2);
}
void solve ()
{
a[0][1]=1;num[0]=1;b[0][1]=0;
for (int u=1;u<=n;u++)
{
for (int i=1;i<=m;i++)//当前的转移节点,我们要尝试吧这个节点转到下一个
{
now^=1;
num[now]=0;
memset(HASH,-1,sizeof(HASH));
for (int k=1;k<=num[now^1];k++)//继承上一个轮廓线的状态
{
int st=b[now^1][k];
LL sum=a[now^1][k];
int p=get(st,i);//获得“竖着的”那个插头状态
int q=get(st,i+1);//获得“横着的”那个插头状态
/*printf("%d %d %lld %d %dn",u,i,sum,p,q);
for (int j=1;j<=3;j++) printf("%d ",get(st,j));
printf("n");*/
if (u==n&&i==1)//是起点 
{
if (p==0&&q==0)
{
change(st,i+1,1);
ADD;
}
else
{
change(st,i+1,0);
ADD;
}
continue;
}
if (u==n&&i==m)//终点
{
if (p>0&&q>0) continue;//不可以有两个插头
if (p==0&&q==0) continue;//不可以没有插头 
ans=ans+sum;
continue;
}
if (map[u][i]==false)//这个是障碍点
{
if (p+q==0) ADD;//要是可以把它绕开就好了 
continue;
}
if (p+q==0)//没有连到当前格子
{
if (map[u][i+1]&&map[u+1][i])
{
change(st,i,1);
change(st,i+1,2);
ADD;
}
}
else if (p==0&&q!=0)
{
if (map[u][i+1]==true) ADD;
if (map[u+1][i]==true)
{
change(st,i,q);
change(st,i+1,0);
ADD;
}
}
else if (p!=0&&q==0)
{
if (map[u+1][i]==true) ADD;
if (map[u][i+1]==true)
{
change(st,i,0);
change(st,i+1,p);
ADD;
}
}
else if (p==1&&q==2)
{
if (u==xx&&i==yy)
ans+=sum;
}
else if (p==2&&q==1)
{
change(st,i,0);
change(st,i+1,0);
ADD;
}
else if (p==1&&q==1)
{
int top=1;
for (int pos=i+2;pos<=m+1;pos++)
{
int temp=get(st,pos);
if (temp==1) top++;
if (temp==2) top--;
if (top==0)
{
change(st,i,0);
change(st,i+1,0);
change(st,pos,1);
ADD;
break;
}
}
}
else if (p==2&&q==2)
{
int top=1;
for (int pos=i-1;pos>0;pos--)
{
int temp=get(st,pos);
if (temp==2) top++;
if (temp==1) top--;
if (top==0)
{
change(st,i,0);
change(st,i+1,0);
change(st,pos,2);
ADD;
break;
}
}
}
}
}
for (int i=1;i<=num[now];i++) b[now][i]<<=2;
}
}
int main()
{
while (true)
{
now=ans=0;
memset(map,false,sizeof(map));
scanf("%d%d",&n,&m);
if (n==0&&m==0) break;
for (int u=1;u<=n;u++)
{
char ss[15];
scanf("%s",ss+1);
for (int i=1;i<=m;i++)
if (ss[i]=='.')
map[u][i]=true;
}
xx=n;yy=m;
solve();
printf("%lldn",ans);
}
return 0;
}

另外的做法

我们可以吧图改变一下
吧n+1行建为.##########.
第n+2行建为……………………..
然后跑回路就可以了
这样的话做法比较妙,并且写起来就是模板

最后

以上就是眼睛大灯泡为你收集整理的[caioj]1491: 基于连通性状态压缩的 动态规划问题:Tony's Tour..我的做法的全部内容,希望文章能够帮你解决[caioj]1491: 基于连通性状态压缩的 动态规划问题:Tony's Tour..我的做法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(76)

评论列表共有 0 条评论

立即
投稿
返回
顶部