2011湖南省ACM大赛G题
来源:互联网 发布:js键值对集合 编辑:程序博客网 时间:2024/04/30 07:18
想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
转载请注明出处:http://blog.csdn.net/wangjian8006
最优对称路径
给一个n 行n 列的网格,每个格子里有一个1 到9 的数字。你需要从左上角走到右下角,其中每一步只能往上、下、左、右四个方向之一走到相邻格子,不能斜着走,也不能走出网格,但可以重复经过一个格子。为了美观,你经过的路径还必须关于“左下-右上”这条对角线对称。下图是一个6x6 网格上的对称路径。
你的任务是统计所有合法路径中,数字之和最小的路径有多少条。
输入
输入最多包含25 组测试数据。每组数据第一行为一个整数n (2<=n<=100 )。以下n 行每行包含n 个1 到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
2 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
- 2011湖南省ACM大赛G题
- 2011湖南省ACM大赛K题
- 2016湖南省程序设计大赛acm : A题
- 2015湖南省ACM大赛F题阶乘除法
- 2015湖南省ACM大赛F题阶乘除法
- 10年第六届湖南省acm程序设计大赛 第一题 A 汽水瓶
- 10年第六届湖南省acm程序设计大赛 第二题 B 弟弟的作业
- 10年第六届湖南省acm程序设计大赛 第三题 C 数字整除
- 10年第六届湖南省acm程序设计大赛 第四题 D 台球碰撞
- 10年第六届湖南省acm程序设计大赛 第五题 E 内部收益率
- 10年第六届湖南省acm程序设计大赛 第三题 C 数字整除
- 第九届湖南省ACM—ICPC程序设计大赛 好老师
- Google is Feeling Lucky(2011上海谷歌杯ACM-ICPC程序大赛G题)
- 山东省第八届acm大赛 G题 (SDUT 3899)
- 2016湖南省赛G题。
- 湖南省2011年程序设计大赛题解
- 湖南省2010程序设计大赛第一题
- csu 1803 湖南省程序设计大赛A题
- HNU 11704 Baidu Post Bar
- 百度2011年9月面试题
- mysql 日期详细操作
- 基于嵌入式Linux的视频采集系统17-----源程序----decoder.h
- problem 11 矩阵中连续四个数的最大积
- 2011湖南省ACM大赛G题
- VC++ INI文件修改,读取键值
- 赵雅智_java 接口
- eclipse快捷键
- UVA 488 - Triangle Wave
- 基于嵌入式Linux的视频采集系统18-----源程序----decoder.cpp
- 概念--进程,进程树,队列,线性表
- nimeideersha
- 基于嵌入式Linux的视频采集系统19-----源程序----v4l_driver.h