1. 程式人生 > >C語言記憶化搜尋___漫步校園(Hdu 1428)

C語言記憶化搜尋___漫步校園(Hdu 1428)

Problem Description LL最近沉迷於AC不能自拔,每天寢室、機房兩點一線。由於長時間坐在電腦邊,缺乏運動。他決定充分利用每次從寢室到機房的時間,在校園裡散散步。整個HDU校園呈方形佈局,可劃分為n*n個小方格,代表各個區域。例如LL居住的18號宿舍位於校園的西北角,即方格(1,1)代表的地方,而機房所在的第三實驗樓處於東南端的(n,n)。因有多條路線可以選擇,LL希望每次的散步路線都不一樣。另外,他考慮從A區域到B區域僅當存在一條從B到機房的路線比任何一條從A到機房的路線更近(否則可能永遠都到不了機房了…)。現在他想知道的是,所有滿足要求的路線一共有多少條。你能告訴他嗎?

Input 每組測試資料的第一行為n(2=<n<=50),接下來的n行每行有n個數,代表經過每個區域所花的時間t(0<t<=50)(由於寢室與機房均在三樓,故起點與終點也得費時)。

Output 針對每組測試資料,輸出總的路線數(小於2^63)。

Sample Input 3 1 2 3 1 2 3 1 2 3 3 1 1 1 1 1 1 1 1 1
Sample Output 1 6

題意:每個點就是一個區域,假如這個區域有一個屬性K就是這個區域到機房的最短距離,那麼LL只能從K大的向K小的區域走(相同也無法走),問一共有幾種走法.

分析:

由於地圖規模達到50×50,直接dfs暴力強搜絕對超時,而且從答案上走法可達到2^63亦可知道,一條條的暴力強搜必然超時.

這個時候我們可以考慮記憶化搜尋(dp+搜尋).

(1)先把每個點到機房的最短距離求出來,這個可以使用最短路徑演算法(Dijjstra.SPFA),也可以用dp,還可以用bfs,以及優先佇列.(只不過這題我用Dijkstra處理的時候不知道是姿勢不對還是怎麼的,超時了,按道理最大規模為2500個點,Dijskstra的時間複雜度為O(n^2)應該也不會超時,搞不懂..)

(2)預處理完,把每個點到機房的最短距離儲存到一個二維陣列中dis[ ][ ];

(3)我們知道走的規矩是隻能從K大向K小的走,那麼假如其中一條路線中的A,B兩點,那麼A,B兩點的路線方向唯一.所以我們根據這點可以得出一個定理:

一個點到機房的有效路徑條數等於它四周相鄰的點的條數之和.

這個時候我們就把搜尋的時間規模降到的地圖大小也就是O(n^2);

四種預處理每個點到機房的最短距離的程式碼():

1.Dijkstra  (儲存在 visit [ ] [ ] )

#include<stdio.h>
#include<string.h>
int time[2501][2501],map[51][51],n,visit[51][51];
int a[4][2]={0,1,1,0,0,-1,-1,0};
int main()
{
    int i,j;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%d",&map[i][j]);
        for(i=0;i<n*n;i++)
            for(j=0;j<n*n;j++)
                if(i==j)
                   time[i][j]=0;
                else
                   time[i][j]=999999;
        for(i=1;i<n;i++)
            time[i-1][i]=time[i][i-1]=map[0][i];
        for(i=1;i<n;i++)
        {
            time[(i-1)*n][i*n]=time[i*n][(i-1)*n]=map[i][0];
            for(j=1;j<n;j++)
            {
                time[i*n+j-1][i*n+j]=time[i*n+j][i*n+j-1]=map[i][j];
                time[(i-1)*n+j][i*n+j]=time[i*n+j][(i-1)*n+j]=map[i][j];
            }
        }                                            //預處理鄰接矩陣time[][]
        int dis[2501],min,u,book[2501]={0};
        for(i=0;i<n*n;i++)
           dis[i]=time[n*n-1][i];
        book[n*n-1]=1;
        for(i=1;i<n*n;i++)              // Dijkstra 演算法核心程式碼
        {
           min=999999;
           for(j=0;j<n*n;j++)
           {
               if(book[j]==0&&dis[j]<min)
                  min=dis[j],u=j;
           }
           book[u]=1;
           for(j=0;j<n*n;j++)
           {
               if(time[u][j]!=999999)
               {
                   if(dis[j]>dis[u]+time[u][j])
                       dis[j]=dis[u]+time[u][j];
               }
           }
        }
        for(i=0;i<n;i++)
           for(j=0;j<n;j++)
               visit[i][j]=dis[i*n+j]+map[i][j];
                
    }
    return 0;
}


2.dp (儲存在 visit [ ] [ ] )

#include<stdio.h>
#include<string.h>
int map[51][51],n,visit[51][51];
int a[4][2]={0,1,1,0,0,-1,-1,0};
int main()
{
    int i,j,flag,k,tx,ty;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                scanf("%d",&map[i][j]);
        for(i=0;i<n;i++)
            for(j=0;j<n;j++)
                visit[i][j]=99999;
        flag=1;
        visit[n-1][n-1]=map[n-1][n-1];
        while(flag)                   //flag為標記變數
        {
            flag=0;
            for(i=n-1;i>=0;i--)
            {
                for(j=n-1;j>=0;j--)
                {
                    for(k=0;k<4;k++)
                    {
                        tx=i+a[k][0];
                        ty=j+a[k][1];
                        if(tx<0||tx>=n||ty<0||ty>=n)
                            continue;
                        if(visit[i][j]>visit[tx][ty]+map[i][j])    //判斷一個點從四個方向上的最近路
                            flag=1,visit[i][j]=visit[tx][ty]+map[i][j];    //一旦有點的資訊發生變化flag標記為1表示要繼續判斷一次.
                    }
                }
            }
        }
    return 0;
}

3.bfs (儲存在dis[ ][ ])
#include<iostream>             
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
typedef __int64 ss;
#define maxx 100000000
struct node
{
    ss x,y;
};
ss n,t[4][2]={1,0,-1,0,0,1,0,-1};
ss dis[100][100],vist[100][100],map[100][100];
void dj()
{
    queue<node>q;
    for(ss i=0;i<=n;i++)
    for(ss j=0;j<=n;j++)
    dis[i][j]=maxx;
    dis[n][n]=map[n][n];
    memset(vist,0,sizeof(vist));
    vist[n][n]=1;
    node tmp;
    tmp.x=n;
    tmp.y=n;
    q.push(tmp);
    while(!q.empty())            //主要思想是用資訊發生變化的點去更新相鄰的點的資訊.由於佇列中都是資訊發生變化的不用向上面dp那樣每個點都搜尋.
    {
        node tmp1=q.front();
        q.pop();
        vist[tmp1.x][tmp1.y]=0;
        for(ss i=0;i<4;i++)
        {
            ss xx=tmp1.x+t[i][0];
            ss yy=tmp1.y+t[i][1];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&dis[xx][yy]>dis[tmp1.x][tmp1.y]+map[xx][yy])
            {
                dis[xx][yy]=dis[tmp1.x][tmp1.y]+map[xx][yy];
                if(!vist[xx][yy])
                {
                    node tmp2;
                    tmp2.x=xx;
                    tmp2.y=yy;
                    q.push(tmp2);
                    vist[xx][yy]=1;      //visit陣列是表示那些點在佇列中.
                }
            }
        }
    }
}

4.優先佇列 (儲存在dis[ ][ ])(時間複雜度最小)

<pre name="code" class="cpp">#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
int dis[51][51],map[51][51],k[4][2]={0,1,1,0,0,-1,-1,0},n;
int visit[51][51];
struct node
{
	int x,y,step;
	friend bool operator < (node a, node b)
    {
        return a.step > b.step;//結構體中,step小的優先順序高
    }

};
priority_queue<node>q;
int main()
{
	int i,j,tx,ty;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=0;i<n;i++)
			for(j=0;j<n;j++)
				scanf("%d",&map[i][j]);
		memset(visit,0,sizeof(visit));
		node a;
		a.x=n-1,a.y=n-1,a.step=map[n-1][n-1];
		q.push(a);
		while(!q.empty())
		{
			node tmp1=q.top();
			q.pop();
			if(visit[tmp1.x][tmp1.y]==0)
				visit[tmp1.x][tmp1.y]=1,dis[tmp1.x][tmp1.y]=tmp1.step;
			else
				continue;
			for(i=0;i<4;i++)
			{
				tx=tmp1.x+k[i][0];
				ty=tmp1.y+k[i][1];
				if(tx<0||tx>=n||ty<0||ty>=n)
					continue;
				node tmp2;
				tmp2.x=tx;
				tmp2.y=ty;
				tmp2.step=tmp1.step+map[tx][ty];
				q.push(tmp2);
			}
		}
	}
	return 0;

}


記憶化搜尋部分程式碼 (預處理在dis[ ][ ],搜尋結果放在dp[ ][ ])
long long dp[51][51];
long long dfs(int x,int y)
{
	if(dp[x][y])return dp[x][y];
	int i,tx,ty;
	for(i=0;i<4;i++)
	{
		tx=x+k[i][0];
		ty=y+k[i][1];
		if(tx<0||ty<0||tx>=n||ty>=n||dis[tx][ty]>=dis[x][y])
			continue;
		dp[x][y]+=dfs(tx,ty);
	}
	return dp[x][y];
}
int main()
{
	int i,j,tx,ty;
	while(scanf("%d",&n)!=EOF)
	{
		……
		memset(dp,0,sizeof(dp));
		dp[n-1][n-1]=1;
		printf("%lld\n",dfs(0,0));
	}
	return 0;

}


Dijkstra預處理超時了,剩下的3個都是AC了的.