2011湖南省ACM大赛G题

来源:互联网 发布:js键值对集合 编辑:程序博客网 时间:2024/04/30 07:18

                                     想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
                                     转载请注明出处:
http://blog.csdn.net/wangjian8006

 

最优对称路径

   给一个列的网格,每个格子里有一个的数字。你需要从左上角走到右下角,其中每一步只能往上、下、左、右四个方向之一走到相邻格子,不能斜着走,也不能走出网格,但可以重复经过一个格子。为了美观,你经过的路径还必须关于“左下-右上”这条对角线对称。下图是一个6x6 网格上的对称路径。

你的任务是统计所有合法路径中,数字之和最小的路径有多少条。

输入

   输入最多包含25 组测试数据。每组数据第一行为一个整数n               2<=n<=100 )。以下行每行包含9的数字,表示输入网格。输入结束标志为n=0

输出

   对于每组数据,输出合法路径中,数字之和最小的路径条数除以1,000,000,009 的余数。

样例输入

2

1 1

1 1

3

1 1 1

1 1 1

2 1 1

0

样例输出:

2

3

 

 

从测试用例来看,一个3*3的网格如下:

1 1 1

1 1

1 1

很明显算出最短路径为5,然后统计有多少条

第一条,(1,1)—>(1,2)—>(1,3)—>(2,3)—>(3,3)

第二条,(1,1)—>(1,2)—>(2,2)—>(2,3)—>(3,3)

第三条,(1,1)—>(2,1)—>(2,2)—>(3,2)—>(3,3)

所以答案就为3

由题目意思来看,既然是对称的路径,那么我们只需要把左上三角的值与右下三角的值加到左上三角上,然后计算从0,0到对称线上的点计算最短路,这样可以大大减少计算时间。

那么存网格的时候可以用二维数组存放起来,以测试用例存放就是:

2 2 1

2 1 1

2 1 1

然后我们看

第一条,(1,1)—>(1,2)—>(2,2)

第二条,(1,1)—>(2,1)—>(2,2)

第三条,(1,1)—>(1,2)—>(1,3)

这样明显减少了计算量。

在网格中,可以直接用dijkstra算法借助队列(STL中的queue)求出其最短路,这样有了最短路就只差最后一步统计了。

最后一步统计的思想是这样的,首先是搜索方法,对于每个“左下-右上”对称线上的点出发,到(0,0)搜索。

标记一个状态dp[x][y],代表x,y到0,0上的最短路有dp[x][y]条,且dp[0][0]=1;设tx,ty的集合是{(x-1,y),(x+1,y),(x,y+1),(x,y-1)},如果0,0到x,y的最短路径等于0,0到tx,ty的最短路径加上 [x][y]这个点网格的值,那么状态转移方程为的dp[x][y]是四个d[tx][ty]之和,当然前提是tx,ty这些点是合理的,没有超过网格界限。

还有一个最重要的剪枝是,当dp[x][y]这个点已经统计过了,那么无需再次统计,用一个flag[x][y]来表示是否是第一次统计,如果是第一次统计的话那么需要计算,如果不是的话直接返回dp[x][y],因为已经统计好了。

 

 

源代码:

#include <iostream>
#include <queue>
using namespace std;
#define inf 100000000
#define MOD 1000000009
#define MAXV 210

int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
//用于bfs和dfs搜索的方向
int map[MAXV][MAXV],vol[MAXV][MAXV],n,dis[MAXV][MAXV],flag[MAXV][MAXV],res;
//map保存网格,vol保存从0,0到i,j的最短路,n表示网格大小
//dis与flag用于深搜剪枝,res保存的是网格的最短路径

bool istrue(int x,int y){ //判断x,y是否是合理的,即不能超过界限
 if(x<n && x>=0 && y<n && y>=0 && y<n-x) return true;
 return false;
}

void dijstra(){
 int x,y,tx,ty,i;
 queue <int>q;

 vol[0][0]=map[0][0];
 q.push(0);
 q.push(0);
 while(!q.empty()){ //求出了所有0,0到i,j的最短路
  x=q.front();q.pop();
  y=q.front();q.pop();

  for(i=0;i<4;i++){
   tx=x+dx[i];
   ty=y+dy[i];
   if(istrue(tx,ty) && vol[tx][ty]>map[tx][ty]+vol[x][y]){
    q.push(tx);
    q.push(ty);
    vol[tx][ty]=map[tx][ty]+vol[x][y];
   }
  }
 }
 res=vol[0][n-1];  //找出0,0到对称线上的最短路的最小值即是整个网格对称路径的最短路
 for(i=0;i<n;i++)
  if(res>vol[i][n-i-1]) res=vol[i][n-i-1];
}

int dfs(int x,int y){
 int i,tx,ty;
 if(x==0 && y==0) return 1;

 //这两句剪枝用的很巧妙,不加的话就会超时,表示x,y是否是第一次搜索,
 //如果不是的话就直接返回,不用再次计算dis
 //dis[x][y]表示从x,y到0,0有多少条这样的路径
 if(flag[x][y]) return dis[x][y];  
 flag[x][y]=true;
 
 dis[x][y]=0;
 for(i=0;i<4;i++){
  tx=x+dx[i];
  ty=y+dy[i];
  if(istrue(tx,ty) && vol[x][y]==vol[tx][ty]+map[x][y]){
   dis[x][y]=(dis[x][y]+dfs(tx,ty))%MOD;
  }
 }
 return dis[x][y];
}

int main(){
 int i,j,key;
 while(scanf("%d",&n) && n){
  for(i=0;i<n;i++)
   for(j=0;j<n;j++)
    scanf("%d",&map[i][j]);
  //优化,把下三角的值加到上三角上面,这样就把问题看成从0,0到对称线上的最短路,可以减少很多计算量
  for(i=0;i<n;vol[i][j]=inf,i++) 
   for(j=0;j<n-i-1;j++){
    map[i][j]=map[i][j]+map[n-j-1][n-i-1];
    vol[i][j]=inf;
   }

  dijstra();  //求出了最短路

  memset(flag,false,sizeof(flag));
  key=0;  //计算相等路有多少条
  for(i=0;i<n;i++)
   if(res==vol[i][n-i-1])
    key=(key+dfs(i,n-i-1))%MOD;
  printf("%d\n",key);
 }
 return 0;
}

 

                                     想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
                                     转载请注明出处:
http://blog.csdn.net/wangjian8006 

原创粉丝点击