vijos 1006 晴天小猪历险记之Hill——数字三角形的终极变化
題目鏈接:https://vijos.org/p/1006
數(shù)字三角形原題看這里:http://www.cnblogs.com/huashanqingzhu/p/7326837.html
背景
在很久很久以前,有一個(gè)動(dòng)物村莊,那里是豬的樂(lè)園(^_^),村民們勤勞、勇敢、善良、團(tuán)結(jié)……
不過(guò)有一天,最小的小小豬生病了,而這種病是極其罕見(jiàn)的,因此大家都沒(méi)有儲(chǔ)存這種藥物。所以晴天小豬自告奮勇,要去采取這種藥草。于是,晴天小豬的傳奇故事便由此展開(kāi)……
描述
這一天,他來(lái)到了一座深山的山腳下,因?yàn)橹挥羞@座深山中的一位隱者才知道這種藥草的所在。但是上山的路錯(cuò)綜復(fù)雜,由于小小豬的病情,晴天小豬想找一條需時(shí)最少的路到達(dá)山頂,但現(xiàn)在它一頭霧水,所以向你求助。
山用一個(gè)三角形表示,從山頂依次向下有1段、2段、3段等山路,每一段用一個(gè)數(shù)字T(1<=T<=100)表示,代表晴天小豬在這一段山路上需要爬的時(shí)間,每一次它都可以朝左、右、左上、右上四個(gè)方向走。山是環(huán)形的。(**注意**:在任意一層的第一段也可以走到本層的最后一段或上一層的最后一段)。
晴天小豬從山的左下角出發(fā),目的地為山頂,即隱者的小屋。
★★★**本題為vijos早年陳題,描述晦澀,現(xiàn)重新描述題面如下**★★★
有一個(gè)數(shù)字三角形,共nn行,依次編號(hào)為第一行,第二行至第nn行。其中第ii行有ii個(gè)數(shù)字,位置依次記為(i,1),(i,2)(i,1),(i,2)到(i,i)(i,i)。
現(xiàn)在從第nn層的第一個(gè)位置出發(fā)(即(n,1)),每一步移到相鄰的,且行編號(hào)小于或等于當(dāng)前行編號(hào)的一個(gè)位置中,直到(1,1)結(jié)束,在不重復(fù)經(jīng)過(guò)任何位置的情形下,路過(guò)的所有位置(包括端點(diǎn))的對(duì)應(yīng)數(shù)字之和最小。
下面詳細(xì)定義相鄰關(guān)系。
同一層內(nèi)連續(xù)的兩個(gè)位置相鄰,特別的有每一層第一個(gè)位置與最后一個(gè)位置相鄰。
對(duì)于位置(i,j),它與(i-1,j-1)以及(i-1,j)相鄰,特別的(i,1)與(i-1,i-1)相鄰,且(i,i)與(i-1,1)相鄰。
格式
輸入格式
第一行有一個(gè)數(shù)n(2<=n<=1000),表示山的高度。
從第二行至第n+1行,第i+1行有i個(gè)數(shù),每個(gè)數(shù)表示晴天小豬在這一段山路上需要爬的時(shí)間。
輸出格式
一個(gè)數(shù),即晴天小豬所需要的最短時(shí)間。
樣例1
樣例輸入1
5
1
2 3
4 5 6
10 1 7 8
1 1 4 5 6 樣例輸出1
10 限制
各個(gè)測(cè)試點(diǎn)1s
提示
在山的兩側(cè)的走法略有特殊,請(qǐng)自己模擬一下,開(kāi)始我自己都弄錯(cuò)了……
來(lái)源
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
?算法一:
建立圖,然后求最短路徑。(來(lái)源)
(從上往下建)
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <cmath> 6 #include <queue> 7 using namespace std; 8 typedef long long ll; 9 const int N=1005*1005/2,INF=1e9+5; 10 inline int read(){ 11 char c=getchar();int x=0,f=1; 12 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 13 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 14 return x*f; 15 } 16 int n,t[N],u,v,w; 17 inline int id(int i,int j){ 18 return i*(i-1)/2+j; 19 } 20 struct edge{ 21 int v,ne; 22 }e[N<<1]; 23 int cnt=0,h[N]; 24 inline void add(int u,int v){ 25 cnt++; 26 e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt; 27 } 28 inline void ins(int u,int v){ 29 add(u,v);add(v,u); 30 } 31 int d[N],inq[N]; 32 queue<int> q; 33 void spfa(int s){ 34 int m=id(n,n); 35 for(int i=1;i<=m;i++) d[i]=INF; 36 d[s]=0; 37 q.push(s); 38 while(!q.empty()){ 39 int u=q.front();q.pop(); 40 inq[u]=0; 41 for(int i=h[u];i;i=e[i].ne){ 42 int v=e[i].v,w=t[u]; 43 if(d[v]>d[u]+w){ 44 d[v]=d[u]+w; 45 if(!inq[v]){inq[v]=1;q.push(v);} 46 } 47 } 48 } 49 } 50 int main(){ 51 n=read(); 52 for(int i=1;i<=n;i++){ 53 if(i!=1) ins(id(i,1),id(i,i)); 54 if(i<n&&i!=1){ 55 add(id(i,i),id(i+1,1)); 56 add(id(i,1),id(i+1,i+1)); 57 } 58 for(int j=1;j<=i;j++){ 59 t[id(i,j)]=read(); 60 if(j<i) ins(id(i,j),id(i,j+1)); 61 if(i<n) add(id(i,j),id(i+1,j)),add(id(i,j),id(i+1,j+1)); 62 } 63 } 64 spfa(id(1,1)); 65 printf("%d",d[id(n,1)]+t[id(n,1)]); 66 }View Code
或者參考下面的這一段代碼:
最短路很容易~
但是建圖有點(diǎn)麻煩~~
因?yàn)榭紤]到如果從一個(gè)點(diǎn)往上連邊的話
會(huì)有點(diǎn)小麻煩(可能不存在~)
那么我們可以從每一個(gè)點(diǎn)開(kāi)始
每一個(gè)可以走到它的點(diǎn)向他連邊
即向下找往上連
如果是在一行的首位置或者末位置就有5種被走上來(lái)的方法
否則四種走法
這個(gè)需要很小心注意一個(gè)地方寫錯(cuò)了就gg(調(diào)了半小時(shí))
好坑的地方就是這里的右上方=上方....
害怕~
然后用個(gè)等差數(shù)列求和公式求出對(duì)應(yīng)的頂標(biāo)就好啦~
?代碼來(lái)源:https://vijos.org/p/1006/solution
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #include <queue> 6 using namespace std; 7 8 const int MAXn=1005; 9 const int MAXN=500501; 10 const int MAXM=2500001; 11 const int INF=(1<<30)-1; 12 struct Edge 13 { 14 int to,w,next; 15 }e[MAXM]; 16 int fisrt[MAXN];//Edges 17 queue<int> q; 18 int d[MAXN],in[MAXN];//SPFA 19 int w[MAXn][MAXn]; 20 int l,n,tot; 21 22 inline void Add_Edge(int x,int y,int w) 23 { 24 e[++tot].to=y; e[tot].w=w; 25 e[tot].next=fisrt[x]; fisrt[x]=tot; 26 } 27 28 inline int getn(int x,int y) 29 { 30 return x*(x-1)/2+y; 31 } 32 33 void init() 34 { 35 memset(fisrt,-1,sizeof(fisrt)); 36 scanf("%d",&l); n=getn(l,l); 37 for(int i=1;i<=l;i++) 38 for(int j=1;j<=i;j++) 39 scanf("%d",&w[i][j]); 40 } 41 42 void getmap()//建圖從上向下建更方便 43 { 44 Add_Edge(2,1,w[2][1]); 45 Add_Edge(3,1,w[2][2]); 46 for(int i=2;i<=l;i++) 47 for(int j=1;j<=i;j++) 48 { 49 int u=getn(i,j); 50 if(j==1) 51 { 52 Add_Edge(getn(i,i),u,w[i][i]); 53 Add_Edge(getn(i,j+1),u,w[i][j+1]); 54 if(i!=l) 55 Add_Edge(getn(i+1,i+1),u,w[i+1][i+1]), 56 Add_Edge(getn(i+1,j),u,w[i+1][j]), 57 Add_Edge(getn(i+1,j+1),u,w[i+1][j+1]); 58 } 59 else if(j==i) 60 { 61 Add_Edge(getn(i,j-1),u,w[i][j-1]); 62 Add_Edge(getn(i,1),u,w[i][1]); 63 if(i!=l) 64 Add_Edge(getn(i+1,j),u,w[i+1][j]), 65 Add_Edge(getn(i+1,1),u,w[i+1][1]), 66 Add_Edge(getn(i+1,j+1),u,w[i+1][j+1]); 67 } 68 else 69 { 70 Add_Edge(getn(i,j-1),u,w[i][j-1]); 71 Add_Edge(getn(i,j+1),u,w[i][j+1]); 72 if(i!=l) 73 Add_Edge(getn(i+1,j),u,w[i+1][j]), 74 Add_Edge(getn(i+1,j+1),u,w[i+1][j+1]); 75 } 76 } 77 } 78 79 void SPFA(int s) 80 { 81 memset(d,0x37,sizeof(d)); 82 q.push(s); d[s]=0; in[s]=1; 83 while(!q.empty()) 84 { 85 int u=q.front(); q.pop(); in[u]=0; 86 for(int i=fisrt[u];i!=-1;i=e[i].next) 87 { 88 int& v=e[i].to; int& w=e[i].w; 89 if(d[v]>d[u]+w) 90 { 91 d[v]=d[u]+w; 92 if(!in[v]) 93 { 94 q.push(v); 95 in[v]=1; 96 } 97 } 98 } 99 } 100 cout<<d[1]+w[1][1]<<endl; 101 } 102 103 int main() 104 { 105 init(); 106 getmap(); 107 SPFA(getn(l,1)); 108 }View Code
?
?算法二:動(dòng)態(tài)規(guī)劃
?說(shuō)實(shí)話,上面那一長(zhǎng)段的關(guān)于動(dòng)規(guī)算法的描述,我是真沒(méi)看懂,直接看代碼還大概清楚一點(diǎn)。
下面代碼中,f[i][j]表示從(i,j)這個(gè)位置到達(dá)頂部(1,1)這個(gè)位置的最短距離。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 7 const int maxn=1005; 8 int a[maxn][maxn]; 9 int f[maxn][maxn]; 10 int n; 11 12 int main() 13 { 14 cin>>n; 15 for(int i=1;i<=n;i++) 16 for(int j=1;j<=i;j++) 17 cin>>a[i][j]; 18 f[1][1]=a[1][1];//終點(diǎn)處就直接是該點(diǎn)時(shí)間 19 for(int i=2;i<=n;i++)//一層一層往上推 20 { 21 for(int j=2;j<i;j++)//先求出從上一層推出來(lái)的最小值 22 f[i][j]=min(f[i-1][j],f[i-1][j-1])+a[i][j]; 23 f[i][1]=min(f[i-1][1],f[i-1][i-1])+a[i][1];//特殊邊界點(diǎn)處理 24 f[i][i]=min(f[i-1][i-1],f[i-1][1])+a[i][i];//特殊邊界點(diǎn)處理 25 //同一層更新最優(yōu)解 26 for(int k=i-1;k>0;k--)//從右往左推 從右往左走的情況更新 27 f[i][k]=min(f[i][k],f[i][k+1]+a[i][k]); 28 f[i][i]=min(f[i][i],f[i][1]+a[i][i]); 29 30 for(int l=2;l<=i;l++)//從左往右推 從左往右走的情況更新 31 f[i][l]=min(f[i][l],f[i][l-1]+a[i][l]); 32 f[i][1]=min(f[i][1],f[i][i]+a[i][1]); 33 //我也沒(méi)太明白為何需要下面這兩個(gè)for,把上面的事情再做一遍。我把下面這兩個(gè)for屏蔽以后確實(shí)是可以AC的。假如真有原因,估計(jì)要讀上面那一長(zhǎng)段文字了 34 for(int k=i-1;k>0;k--)//再推一遍從右往左推 從右往左走的情況更新 35 f[i][k]=min(f[i][k],f[i][k+1]+a[i][k]); 36 f[i][i]=min(f[i][i],f[i][1]+a[i][i]); 37 38 for(int l=2;l<=i;l++)//再推一遍從左往右推 從左往右走的情況更新 39 f[i][l]=min(f[i][l],f[i][l-1]+a[i][l]); 40 f[i][1]=min(f[i][1],f[i][i]+a[i][1]); 41 } 42 cout<<f[n][1]<<endl; 43 }
上面這一段代碼來(lái)自vijos討論版塊:https://vijos.org/p/1006/solution
下面是另一個(gè)動(dòng)規(guī)代碼,可以與上面這一段互相對(duì)照理解。代碼來(lái)源:http://blog.csdn.net/Little_Flower_0/article/details/47945611
下面這段代碼中,d[i][j]表示從(i,j)這個(gè)位置到達(dá)底層的最短距離。
1 #include<stdio.h> 2 #define maxint 2000000000 3 #define min(a,b) (a<b?a:b) 4 long a[1000][1000]={0}; 5 long d[1000][1000]={0}; 6 int main() 7 { 8 long n,i,j; 9 scanf("%ld",&n); 10 for(i=0;i<n;i++) 11 for(j=0;j<=i;j++) 12 scanf("%ld",&a[i][j]); 13 14 for(i=0;i<n;i++) 15 for(j=0;j<=i;j++) 16 d[i][j]=maxint; 17 18 for(i=0;i<n;i++) //對(duì)最后一行的處理 19 d[n-1][i]=a[n-1][i]; 20 for(i=1;i<n;i++) 21 d[n-1][i]=d[n-1][i-1]+a[n-1][i]; //因?yàn)樽詈笠恍杏疫叺狞c(diǎn)只能從左邊的推來(lái),所以有了這個(gè)預(yù)處理 22 for(i=n-1;i>=0;i--) 23 d[n-1][i]=min(d[n-1][i],d[n-1][(i+1)%n]+a[n-1][i]); //往左走的話,肯定要先從左邊翻過(guò)去再向左走 24 25 for(i=n-2;i>=0;i--) 26 { 27 d[i][0]=min(d[i+1][0],d[i+1][1]); //對(duì)左邊界的處理 28 d[i][0]=min(d[i][0],d[i+1][i+1]); 29 d[i][0]+=a[i][0]; 30 31 d[i][i]=min(d[i+1][0],d[i+1][i]); //對(duì)右邊界的處理 32 d[i][i]=min(d[i][i],d[i+1][i+1]); 33 d[i][i]+=a[i][i]; 34 35 for(j=1;j<=i-1;j++) //對(duì)中間位置的處理,這時(shí)候下面的一行已經(jīng)處理完了 36 d[i][j]=min(d[i+1][j],d[i+1][j+1])+a[i][j]; 37 38 d[i][0]=min(d[i][0],d[i][i]+a[i][0]); //左推與右推 39 for(j=1;j<=i;j++) 40 d[i][j]=min(d[i][j],d[i][j-1]+a[i][j]); 41 for(j=i;j>=0;j--) 42 d[i][j]=min(d[i][j],d[i][(j+1)%(i+1)]+a[i][j]); 43 44 } 45 printf("%ld\n",d[0][0]); 46 return 0; 47 }
?
轉(zhuǎn)載于:https://www.cnblogs.com/huashanqingzhu/p/7345648.html
總結(jié)
以上是生活随笔為你收集整理的vijos 1006 晴天小猪历险记之Hill——数字三角形的终极变化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 微信空白网名复制2016
- 下一篇: 微信一次性订阅消息