vijos 1006 晴天小猪历险记之Hill——数字三角形的终极变化
题目链接:https://vijos.org/p/1006
数字三角形原题看这里:http://www.cnblogs.com/huashanqingzhu/p/7326837.html
背景
在很久很久以前,有一个动物村庄,那里是猪的乐园(^_^),村民们勤劳、勇敢、善良、团结……
不过有一天,最小的小小猪生病了,而这种病是极其罕见的,因此大家都没有储存这种药物。所以晴天小猪自告奋勇,要去采取这种药草。于是,晴天小猪的传奇故事便由此展开……
描述
这一天,他来到了一座深山的山脚下,因为只有这座深山中的一位隐者才知道这种药草的所在。但是上山的路错综复杂,由于小小猪的病情,晴天小猪想找一条需时最少的路到达山顶,但现在它一头雾水,所以向你求助。
山用一个三角形表示,从山顶依次向下有1段、2段、3段等山路,每一段用一个数字T(1<=T<=100)表示,代表晴天小猪在这一段山路上需要爬的时间,每一次它都可以朝左、右、左上、右上四个方向走。山是环形的。(**注意**:在任意一层的第一段也可以走到本层的最后一段或上一层的最后一段)。
晴天小猪从山的左下角出发,目的地为山顶,即隐者的小屋。
★★★**本题为vijos早年陈题,描述晦涩,现重新描述题面如下**★★★
有一个数字三角形,共nn行,依次编号为第一行,第二行至第nn行。其中第ii行有ii个数字,位置依次记为(i,1),(i,2)(i,1),(i,2)到(i,i)(i,i)。
现在从第nn层的第一个位置出发(即(n,1)),每一步移到相邻的,且行编号小于或等于当前行编号的一个位置中,直到(1,1)结束,在不重复经过任何位置的情形下,路过的所有位置(包括端点)的对应数字之和最小。
下面详细定义相邻关系。
同一层内连续的两个位置相邻,特别的有每一层第一个位置与最后一个位置相邻。
对于位置(i,j),它与(i-1,j-1)以及(i-1,j)相邻,特别的(i,1)与(i-1,i-1)相邻,且(i,i)与(i-1,1)相邻。
格式
输入格式
第一行有一个数n(2<=n<=1000),表示山的高度。
从第二行至第n+1行,第i+1行有i个数,每个数表示晴天小猪在这一段山路上需要爬的时间。
输出格式
一个数,即晴天小猪所需要的最短时间。
样例1
样例输入1
5
1
2 3
4 5 6
10 1 7 8
1 1 4 5 6
样例输出1
10
限制
各个测试点1s
提示
在山的两侧的走法略有特殊,请自己模拟一下,开始我自己都弄错了……
来源
Sunnypig
算法分析:
参考:
http://blog.csdn.net/Little_Flower_0/article/details/47945611
https://vijos.org/p/1006/solution
http://www.cnblogs.com/candy99/p/5981533.html
算法一:
建立图,然后求最短路径。(来源)
(从上往下建)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;
typedef long long ll;
const int N=*/,INF=1e9+;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int n,t[N],u,v,w;
inline int id(int i,int j){
return i*(i-)/+j;
}
struct edge{
int v,ne;
}e[N<<];
int cnt=,h[N];
inline void add(int u,int v){
cnt++;
e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
}
inline void ins(int u,int v){
add(u,v);add(v,u);
}
int d[N],inq[N];
queue<int> q;
void spfa(int s){
int m=id(n,n);
for(int i=;i<=m;i++) d[i]=INF;
d[s]=;
q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
inq[u]=;
for(int i=h[u];i;i=e[i].ne){
int v=e[i].v,w=t[u];
if(d[v]>d[u]+w){
d[v]=d[u]+w;
if(!inq[v]){inq[v]=;q.push(v);}
}
}
}
}
int main(){
n=read();
for(int i=;i<=n;i++){
if(i!=) ins(id(i,),id(i,i));
if(i<n&&i!=){
add(id(i,i),id(i+,));
add(id(i,),id(i+,i+));
}
for(int j=;j<=i;j++){
t[id(i,j)]=read();
if(j<i) ins(id(i,j),id(i,j+));
if(i<n) add(id(i,j),id(i+,j)),add(id(i,j),id(i+,j+));
}
}
spfa(id(,));
printf("%d",d[id(n,)]+t[id(n,)]);
}
或者参考下面的这一段代码:
最短路很容易~
但是建图有点麻烦~~
因为考虑到如果从一个点往上连边的话
会有点小麻烦(可能不存在~)
那么我们可以从每一个点开始
每一个可以走到它的点向他连边
即向下找往上连
如果是在一行的首位置或者末位置就有5种被走上来的方法
否则四种走法
这个需要很小心注意一个地方写错了就gg(调了半小时)
好坑的地方就是这里的右上方=上方....
害怕~
然后用个等差数列求和公式求出对应的顶标就好啦~
代码来源:https://vijos.org/p/1006/solution
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std; const int MAXn=;
const int MAXN=;
const int MAXM=;
const int INF=(<<)-;
struct Edge
{
int to,w,next;
}e[MAXM];
int fisrt[MAXN];//Edges
queue<int> q;
int d[MAXN],in[MAXN];//SPFA
int w[MAXn][MAXn];
int l,n,tot; inline void Add_Edge(int x,int y,int w)
{
e[++tot].to=y; e[tot].w=w;
e[tot].next=fisrt[x]; fisrt[x]=tot;
} inline int getn(int x,int y)
{
return x*(x-)/+y;
} void init()
{
memset(fisrt,-,sizeof(fisrt));
scanf("%d",&l); n=getn(l,l);
for(int i=;i<=l;i++)
for(int j=;j<=i;j++)
scanf("%d",&w[i][j]);
} void getmap()//建图从上向下建更方便
{
Add_Edge(,,w[][]);
Add_Edge(,,w[][]);
for(int i=;i<=l;i++)
for(int j=;j<=i;j++)
{
int u=getn(i,j);
if(j==)
{
Add_Edge(getn(i,i),u,w[i][i]);
Add_Edge(getn(i,j+),u,w[i][j+]);
if(i!=l)
Add_Edge(getn(i+,i+),u,w[i+][i+]),
Add_Edge(getn(i+,j),u,w[i+][j]),
Add_Edge(getn(i+,j+),u,w[i+][j+]);
}
else if(j==i)
{
Add_Edge(getn(i,j-),u,w[i][j-]);
Add_Edge(getn(i,),u,w[i][]);
if(i!=l)
Add_Edge(getn(i+,j),u,w[i+][j]),
Add_Edge(getn(i+,),u,w[i+][]),
Add_Edge(getn(i+,j+),u,w[i+][j+]);
}
else
{
Add_Edge(getn(i,j-),u,w[i][j-]);
Add_Edge(getn(i,j+),u,w[i][j+]);
if(i!=l)
Add_Edge(getn(i+,j),u,w[i+][j]),
Add_Edge(getn(i+,j+),u,w[i+][j+]);
}
}
} void SPFA(int s)
{
memset(d,0x37,sizeof(d));
q.push(s); d[s]=; in[s]=;
while(!q.empty())
{
int u=q.front(); q.pop(); in[u]=;
for(int i=fisrt[u];i!=-;i=e[i].next)
{
int& v=e[i].to; int& w=e[i].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
if(!in[v])
{
q.push(v);
in[v]=;
}
}
}
}
cout<<d[]+w[][]<<endl;
} int main()
{
init();
getmap();
SPFA(getn(l,));
}
算法二:动态规划
说实话,上面那一长段的关于动规算法的描述,我是真没看懂,直接看代码还大概清楚一点。
下面代码中,f[i][j]表示从(i,j)这个位置到达顶部(1,1)这个位置的最短距离。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std; const int maxn=;
int a[maxn][maxn];
int f[maxn][maxn];
int n; int main()
{
cin>>n;
for(int i=;i<=n;i++)
for(int j=;j<=i;j++)
cin>>a[i][j];
f[][]=a[][];//终点处就直接是该点时间
for(int i=;i<=n;i++)//一层一层往上推
{
for(int j=;j<i;j++)//先求出从上一层推出来的最小值
f[i][j]=min(f[i-][j],f[i-][j-])+a[i][j];
f[i][]=min(f[i-][],f[i-][i-])+a[i][];//特殊边界点处理
f[i][i]=min(f[i-][i-],f[i-][])+a[i][i];//特殊边界点处理
//同一层更新最优解
for(int k=i-;k>;k--)//从右往左推 从右往左走的情况更新
f[i][k]=min(f[i][k],f[i][k+]+a[i][k]);
f[i][i]=min(f[i][i],f[i][]+a[i][i]); for(int l=;l<=i;l++)//从左往右推 从左往右走的情况更新
f[i][l]=min(f[i][l],f[i][l-]+a[i][l]);
f[i][]=min(f[i][],f[i][i]+a[i][]);
//我也没太明白为何需要下面这两个for,把上面的事情再做一遍。我把下面这两个for屏蔽以后确实是可以AC的。假如真有原因,估计要读上面那一长段文字了
for(int k=i-;k>;k--)//再推一遍从右往左推 从右往左走的情况更新
f[i][k]=min(f[i][k],f[i][k+]+a[i][k]);
f[i][i]=min(f[i][i],f[i][]+a[i][i]); for(int l=;l<=i;l++)//再推一遍从左往右推 从左往右走的情况更新
f[i][l]=min(f[i][l],f[i][l-]+a[i][l]);
f[i][]=min(f[i][],f[i][i]+a[i][]);
}
cout<<f[n][]<<endl;
}
上面这一段代码来自vijos讨论版块:https://vijos.org/p/1006/solution
下面是另一个动规代码,可以与上面这一段互相对照理解。代码来源:http://blog.csdn.net/Little_Flower_0/article/details/47945611
下面这段代码中,d[i][j]表示从(i,j)这个位置到达底层的最短距离。
#include<stdio.h>
#define maxint 2000000000
#define min(a,b) (a<b?a:b)
long a[][]={};
long d[][]={};
int main()
{
long n,i,j;
scanf("%ld",&n);
for(i=;i<n;i++)
for(j=;j<=i;j++)
scanf("%ld",&a[i][j]); for(i=;i<n;i++)
for(j=;j<=i;j++)
d[i][j]=maxint; for(i=;i<n;i++) //对最后一行的处理
d[n-][i]=a[n-][i];
for(i=;i<n;i++)
d[n-][i]=d[n-][i-]+a[n-][i]; //因为最后一行右边的点只能从左边的推来,所以有了这个预处理
for(i=n-;i>=;i--)
d[n-][i]=min(d[n-][i],d[n-][(i+)%n]+a[n-][i]); //往左走的话,肯定要先从左边翻过去再向左走 for(i=n-;i>=;i--)
{
d[i][]=min(d[i+][],d[i+][]); //对左边界的处理
d[i][]=min(d[i][],d[i+][i+]);
d[i][]+=a[i][]; d[i][i]=min(d[i+][],d[i+][i]); //对右边界的处理
d[i][i]=min(d[i][i],d[i+][i+]);
d[i][i]+=a[i][i]; for(j=;j<=i-;j++) //对中间位置的处理,这时候下面的一行已经处理完了
d[i][j]=min(d[i+][j],d[i+][j+])+a[i][j]; d[i][]=min(d[i][],d[i][i]+a[i][]); //左推与右推
for(j=;j<=i;j++)
d[i][j]=min(d[i][j],d[i][j-]+a[i][j]);
for(j=i;j>=;j--)
d[i][j]=min(d[i][j],d[i][(j+)%(i+)]+a[i][j]); }
printf("%ld\n",d[][]);
return ;
}
vijos 1006 晴天小猪历险记之Hill——数字三角形的终极变化的更多相关文章
- Vijos 1006 晴天小猪历险记之Hill 单源单汇最短路
背景 在很久很久以前,有一个动物村庄,那里是猪的乐园(^_^),村民们勤劳.勇敢.善良.团结-- 不过有一天,最小的小小猪生病了,而这种病是极其罕见的,因此大家都没有储存这种药物.所以晴天小猪自告奋勇 ...
- 【vijos】1006 晴天小猪历险记之Hill(dijkstra)
https://vijos.org/p/1006 连边后跑点权的最短路 注意连边的时候左端点可以连到下一行的右端点,右端点可以连到下一行的左端点 #include <cstdio> #in ...
- Vijos1006P1006晴天小猪历险记之Hill[最短路]
P1006晴天小猪历险记之Hill Accepted 标签:晴天小猪历险记[显示标签] 背景 在很久很久以前,有一个动物村庄,那里是猪的乐园(^_^),村民们勤劳.勇敢.善良.团结……不过有一 ...
- vijosP1006 晴天小猪历险记之Hill
vijosP1006 晴天小猪历险记之Hill 链接:https://vijos.org/p/1006 [思路] 图上DP. 这个题的递推顺序是关键.先从上一行得到最小值,然后从本行比较最小值,注意本 ...
- 晴天小猪历险记之Hill(Dijkstra优先队列优化)
描述 这一天,他来到了一座深山的山脚下,因为只有这座深山中的一位隐者才知道这种药草的所在.但是上山的路错综复杂,由于小小猪的病情,晴天小猪想找一条需时最少的路到达山顶,但现在它一头雾水,所以向你求助. ...
- G:数字三角形
总时间限制: 1000ms 内存限制: 65536kB描述73 88 1 02 7 4 44 5 2 6 5 (图1) 图1给出了一个数字三角形.从三角形的顶部 ...
- 4829 [DP]数字三角形升级版
4829 [DP]数字三角形升级版 时间限制: 1 s 空间限制: 16000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 从数字三角形的顶部(如图, ...
- 【递归】数字三角形 简单dp
[递归]数字三角形 题目描述 对于大多数人来说,“我们是这么的正常,因此也就这么的平庸.”而天才总是与众不同的,所以当邪狼问修罗王:“老大,你蹲在那儿一动不动看了有半个小时了,蚂蚁有那么好看吗?” 修 ...
- hihocoder 1037 数字三角形
#1037 : 数字三角形 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 问题描述 小Hi和小Ho在经历了螃蟹先生的任务之后被奖励了一次出国旅游的机会,于是他们来到了大洋彼岸 ...
随机推荐
- SQL Server 数据库项目
ylbtech-.NET Framework: SQL Server 数据库项目 SQL Server 数据库项目 类型:SQL Server 用于创建 SQL Server 数据库的项目 1. 新建 ...
- C++/Php/Python 语言执行shell命令
编程中经常需要在程序中使用shell命令来简化程序,这里记录一下. 1. C++ 执行shell命令 #include <iostream> #include <string> ...
- IIS7.5中调试.Net 4.0网站出现无厘头500错误的解决办法 (转)
刚刚 部署了ii7的dll的有x86写的,就会出现以下这样的问题 iis 7 x86,Could not load file or assembly 'Name' or one of its depe ...
- Windbg简明教程(转)
Windbg是Microsoft公司免费调试器调试集合中的GUI的调试器,支持Source和Assembly两种模式的调试.Windbg不仅可以调试应用程序,还可以进行Kernel Debug(新版本 ...
- ASP.NET网站管理工具的【安全】功能无法使用问题
在使用ASP.NET网站管理工具时,安全出现下面的问题: 出现这种情况的主要原因是,安全管理中需要创建用户和角色信息,所以要用到数据库,但是你没有设置好数据库. 可以打开vs自带的命令提示工具: 打开 ...
- sql server 由于登入失败而无法启动服务
到控制面板——管理工具——服务,找到mssqlserver这个服务,在属性里把登陆帐户改成你目前登录windows的帐户或选择本地系统账户再重新启动服务就好了
- 转:TensorFlow入门(六) 双端 LSTM 实现序列标注(分词)
http://blog.csdn.net/Jerr__y/article/details/70471066 欢迎转载,但请务必注明原文出处及作者信息. @author: huangyongye @cr ...
- GPUImage API文档之GPUImageInput协议
GPUImageInput协议主要包含一些输入需要渲染目标的操作. - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)t ...
- 详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点
转自:http://www.jb51.net/article/100111.htm 首先我们需要安装好Nginx.jdk.Tomcat,安装方法已经在 上一篇 说过了,本篇不再赘述. 下来看一下我们的 ...
- Tone Mapping算法系列一:基于Fast Bilateral Filtering 算法的 High-Dynamic Range(HDR) 图像显示技术。
一.引言 本人初次接触HDR方面的知识,有描述不正确的地方烦请见谅. 为方便文章描述,引用部分百度中的文章对HDR图像进行简单的描述. 高动态范围图像(High-Dynamic Range,简称HDR ...