poj1077(A*,IDA*)

1.资料请参考刘汝佳的黑书,组合数学。


A*使用了h(s)做为在结点s下的代价下界值,转移时有h(s1)>=h(s2)+c(s1+s2),其中c(s1,s2)为状态s1转移到状态s2时所花费的代价!大家仔细揣摩一下这个方程,这个方程保证了A*算法的正确性(个人理解)

当s1,s2为相邻状态时有h(s1)>=h(s2)+1

然后用一个优先队列维护待扩展的结点,优先级判断的依据是f(s),f(s)=g(s)+h(s),g(s)表示从根结点到s结点花费的代价,h(s)为s结点的代价下界估计值。

个人感觉IDA*还好理解一些,它的核心思想就是用了一个变量pathLimit限制了搜索的深度,若成功则肯定是最优的,若不成功,则增加pathLimit直到有解!


2.A*实现

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
using namespace std;

int p[9]= {1,1,2,6,24,120,720,5040,40320};
int encode(int *perm)//n=9
{
    //八数码问题,九个格子,相当于总的排列数为9!
    int sum=0;
    for(int i=0; i<9; i++)
    {
        int count=0;
        for(int j=0; j<i; j++)
        {
            if(perm[j]>perm[i])count++;
        }
        sum+=count*p[9-perm[i]];
    }
    return sum;
}

void decode(int sum,int *perm)
{
    for(int i=0; i<9; i++)perm[i]=0;

    for(int i=1; i<=9; i++)
    {
        int tot=sum/p[9-i]+1;
        sum%=p[9-i];
        int loc,count=0;
        for(loc=0; loc<9; loc++)
        {
            if(!perm[loc])
            {
                count++;
                if(count==tot)break;//!!找到空第tot个空位置
            }
        }
        perm[loc]=i;
    }
}

void initial(int *maze,int &space)
{
    char grid;
    for(int i=0; i<9; i++)
    {
        cin>>grid;
        if(grid!='x')maze[i]=grid-'1'+1;
        else
        {
            space=i;
            maze[i]=9;
        }
    }
}
#define STATE 362890
struct Node
{
    int preState;
    int step;
    int space;
    int h;
} node[STATE];
bool vis[STATE];

bool isResolve(int *maze,int space)
{
    int s=abs(double(2-space/3))+abs(double(2-space%3));
    for(int i=0; i<9; i++)
    {
        for(int j=0; j<i; j++)
        {
            if(maze[i]>maze[j])s++;
        }
    }
    if(s&1)return false;
    else return true;
}
int h(int *maze)
{
    int h=0;
    for(int i=0; i<9; i++)
    {
        if(maze[i]!=9)h+=abs(double((maze[i]-1)/3-i/3))+abs(double( (maze[i]-1)%3-i%3));
    }
    return h;
}

int next[9][4]=
{
    {-1,3,1,-1},
    {0,4,2,-1},
    {1,5,-1,-1},
    {-1,6,4,0},
    {3,7,5,1},
    {4,8,-1,2},
    {-1,-1,7,3},
    {6,-1,8,4},
    {7,-1,-1,5}
};
class cmp
{
public:
    bool operator()(int sa,int sb)
    {
        return node[sa].h+node[sa].step>node[sb].h+node[sb].step;
    }
};
bool AStar(int *maze,int space)
{
    if(!isResolve(maze,space))return false;

    memset(vis,0,sizeof(vis));
    priority_queue<int,vector<int>,cmp> que;
    int curState=encode(maze),nextState;
    node[curState].preState=-1;
    node[curState].step=0;
    node[curState].space=space;
    node[curState].h=h(maze);

    que.push(curState);

    while(!que.empty())
    {
        curState=que.top();
        que.pop();
        if(!curState)break;//目标状态
        if(vis[curState])continue;
        vis[curState]=true;
        decode(curState,maze);
        int cur=node[curState].space;
        for(int i=0; i<4; i++)
        {
            if(next[cur][i]==-1)continue;
            swap(maze[cur],maze[ next[cur][i] ] );
            nextState=encode(maze);
            if(!vis[nextState] || node[nextState].step>node[curState].step+1)
            {
                node[nextState].preState=curState;
                node[nextState].space=next[cur][i];
                node[nextState].step=node[curState].step+1;
                node[nextState].h=h(maze);
                que.push(nextState);
            }
            swap(maze[cur],maze[ next[cur][i] ] );
        }
    }
    return true;
}
char dir[4]=
{
    'l','d','r','u'
};
void output(int curState,int *maze)
{
    decode(curState,maze);
    int preState=node[curState].preState;

    if(preState==-1)return;

    output(preState,maze);
    for(int i=0; i<4; i++)
    {
        if(next[node[preState].space][i]==node[curState].space)
        {
            cout<<dir[i];
            break;
        }
    }
}

int main()
{
    int maze[9],space;
    initial(maze,space);
    if(AStar(maze,space))output(0,maze);
    else cout<<"unsolvable";
    cout<<endl;
    return 0;
}


3.IDA*(其实这个算法比我们想像中的要简单得多,我猜大家直接看代码就能看懂!!!)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;

void initial(int *maze,int &space)
{
    char grid;
    for(int i=0; i<9; i++)
    {
        cin>>grid;
        if(grid!='x')maze[i]=grid-'1'+1;
        else
        {
            space=i;
            maze[i]=9;
        }
    }
}

bool isResolve(int *maze,int space)
{
    int s=abs(double(2-space/3))+abs(double(2-space%3));
    for(int i=0; i<9; i++)
    {
        for(int j=0; j<i; j++)
        {
            if(maze[i]>maze[j])s++;
        }
    }
    if(s&1)return false;
    else return true;
}
int h(int *maze)
{
    int h=0;
    for(int i=0; i<9; i++)
    {
        if(maze[i]!=9) h+=abs(double((maze[i]-1)/3-i/3))+abs(double( (maze[i]-1)%3-i%3));
    }
    return h;
}

int next[9][4]=
{
    {-1,3,1,-1},
    {0,4,2,-1},
    {1,5,-1,-1},
    {-1,6,4,0},
    {3,7,5,1},
    {4,8,-1,2},
    {-1,-1,7,3},
    {6,-1,8,4},
    {7,-1,-1,5}
};

bool isAns(int *maze)
{
    for(int i=0; i<8; i++)if(maze[i+1]-maze[i]!=1)return false;
    return true;
}
int pathLimit;
int path[362890],pathLen;

bool IDAStar(int *maze,int len,int space)
{
    if(len==pathLimit)
    {
        if(isAns(maze))
        {
            pathLen=len;
            return true;
        }
        return false;
    }
    for(int i=0; i<4; i++)
    {
        if(next[space][i]!=-1)
        {
            if(len>0&&abs(double(i-path[len-1]))==2)continue;//!!不考虑相反的方向
            swap(maze[space],maze[next[space][i]]);

            path[len]=i;
            if(h(maze)+len<=pathLimit&&IDAStar(maze,len+1,next[space][i]))
                return true;
            swap(maze[space],maze[next[space][i]]);

        }
    }
    return false;
}
char dir[4]= {'l','d','r','u'};
void output()
{
    for(int i=0; i<pathLen; i++)
    {
        switch(path[i])
        {
        case 0:
            cout<<'l';
            break;
        case 1:
            cout<<'d';
            break;
        case 2:
            cout<<'r';
            break;
        case 3:
            cout<<'u';
        }
    }
}
int main()
{
    int maze[9],space;
    initial(maze,space);

    pathLimit=h(maze);
    if(isResolve(maze,space))
    {
        while(!IDAStar(maze,0,space))pathLimit++;
        output();
    }
    else cout<<"unsolvable";
    cout<<endl;
    return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一道比较经典的计数问题。题目描述如下: 给定一个 $n \times n$ 的网格图,其中一些格子被标记为障碍。一个连通块是指一些被标记为障碍的格子的集合,满足这些格子在网格图中连通。一个格子是连通的当且仅当它与另一个被标记为障碍的格子在网格图中有公共边。 现在,你需要计算在这个网格图中,有多少个不同的连通块,满足这个连通块的大小(即包含的格子数)恰好为 $k$。 这是一道比较经典的计数问题,一般可以通过计算生成函数的方法来解决。具体来说,我们可以定义一个生成函数 $F(x)$,其中 $[x^k]F(x)$ 表示大小为 $k$ 的连通块的个数。那么,我们可以考虑如何计算这个生成函数。 对于一个大小为 $k$ 的连通块,我们可以考虑它的形状。具体来说,我们可以考虑以该连通块的最左边、最上边的格子为起点,从上到下、从左到右遍历该连通块,把每个格子在该连通块中的相对位置记录下来。由于该连通块的大小为 $k$,因此这些相对位置一定是 $(x,y) \in [0,n-1]^2$ 中的 $k$ 个不同点。 现在,我们需要考虑如何计算这些点对应的连通块是否合法。具体来说,我们可以考虑从左到右、从上到下依次处理这些点,对于每个点 $(x,y)$,我们需要考虑它是否能够与左边的点和上边的点连通。具体来说,如果 $(x-1,y)$ 和 $(x,y)$ 都在该连通块中且它们在网格图中有公共边,那么它们就是连通的;同样,如果 $(x,y-1)$ 和 $(x,y)$ 都在该连通块中且它们在网格图中有公共边,那么它们也是连通的。如果 $(x,y)$ 与左边和上边的点都不连通,那么说明这个点不属于该连通块。 考虑到每个点最多只有两个方向需要检查,因此时间复杂度为 $O(n^2 k)$。不过,我们可以使用类似于矩阵乘法的思想,将这个过程优化到 $O(k^3)$ 的时间复杂度。 具体来说,我们可以设 $f_{i,j,k}$ 表示状态 $(i,j)$ 所代表的点在连通块中,且连通块的大小为 $k$ 的方案数。显然,对于一个合法的 $(i,j,k)$,我们可以考虑 $(i-1,j,k-1)$ 和 $(i,j-1,k-1)$ 这两个状态,然后把点 $(i,j)$ 加入到它们所代表的连通块中。因此,我们可以设计一个 $O(k^3)$ 的 DP 状态转移,计算 $f_{i,j,k}$。 具体来说,我们可以考虑枚举连通块所包含的最右边和最下边的格子的坐标 $(x,y)$,然后计算 $f_{x,y,k}$。对于一个合法的 $(x,y,k)$,我们可以考虑将 $(x,y)$ 所代表的点加入到 $(x-1,y,k-1)$ 和 $(x,y-1,k-1)$ 所代表的连通块中。不过,这里需要注意一个细节:如果 $(x-1,y)$ 和 $(x,y)$ 在网格图中没有相邻边,那么它们不能算作连通的。因此,我们需要特判这个情况。 最终,$f_{n,n,k}$ 就是大小为 $k$ 的连通块的个数,时间复杂度为 $O(n^2 k + k^3)$。 参考代码:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值