數字三角形【匯總】


1220 數字三角形
題目鏈接http://codevs.cn/problem/1220/
題目描述  Description

如圖所示的數字三角形,從頂部出發,在每一結點可以選擇向左走或得向右走,一直走到底層,要求找出一條路徑,使路徑上的值最大。

輸入描述  Input Description

第一行是數塔層數N(1<=N<=100)。

第二行起,按數塔圖形,有一個或多個的整數,表示該層節點的值,共有N行。

輸出描述  Output Description

輸出最大值。

樣例輸入  Sample Input

5

13

11 8

12 7 26

6 14 15 8

12 7 13 24 11

樣例輸出  Sample Output

86

解題思路:

參考北大郭煒老師

用二維數組存放數字三角形。
D( r, j) : r行第 j 個數字(r,j1開始算)
MaxSum(r, j) : D(r,j)到底邊的各條路徑中,最佳路徑的數字之和。
問題:求 MaxSum(1,1)
典型的遞歸問題。
D(r, j)出發,下一步只能走D(r+1,j)或者D(r+1, j+1)。故對於N行的三角形:
if ( r == N)  MaxSum(r,j) = D(r,j)
else  MaxSum( r, j) = Max{ MaxSum(r1,j), MaxSum(r+1,j+1) }  + D(r,j)

 

下面四段代碼分別是存粹遞歸、記憶型遞歸、遞推、空間優化的遞推的代碼:

 1 #include <stdio.h>
 2 
 3 #define maxN 101
 4 
 5 int N;
 6 int D[maxN][maxN];      //D[i][j]表示第i行第 j 個數字。其中i、j從1開始算
 7 
 8 int maxSum[maxN][maxN]; //maxSum[i][j]表示從D(i,j)到底邊的各條路徑中,最佳路徑的數字之和。
 9 
10 
11 //代碼一:純粹遞歸。當N達到100是絕對是超時的。因為復雜度是O(2^N) 
12 int fun1(int i,int j)//返回從(i,j)到達最底層的最大路徑之和 
13 {
14     if(i==N) return D[i][j];
15     else
16     {
17         int x=fun1(i+1,j);
18         int y=fun1(i+1,j+1);
19         return D[i][j]+(x>y?x:y);
20     }
21 }
22 
23 //代碼二:記憶型遞歸,避免重復計算。時間復雜度O(n*n) 
24 int fun2(int i,int j)//返回從(i,j)到達最底層的最大路徑之和 
25 {
26     if(maxSum[i][j]!=-1) return maxSum[i][j];
27     
28     if(i==N) maxSum[i][j]=D[i][j];
29     else
30     {
31         int x=fun2(i+1,j);
32         int y=fun2(i+1,j+1);
33         maxSum[i][j]=D[i][j]+(x>y?x:y);
34     }
35     
36     return maxSum[i][j];
37 }
38 
39 //代碼三:遞歸變遞推 
40 int fun3()
41 {
42     int i,j;
43     for(j=1;j<=N;j++) maxSum[N][j]=D[N][j];
44     
45     for(i=N-1;i>=1;i--)
46     {
47         for(j=1;j<=i;j++)
48         {
49             int max=(maxSum[i+1][j]>maxSum[i+1][j+1]?maxSum[i+1][j]:maxSum[i+1][j+1]);
50             maxSum[i][j]=D[i][j]+max;
51         }
52     }
53     return maxSum[1][1];
54 }
55 
56 //代碼四:遞歸變遞推並在空間上做優化
57 int fun4()
58 {
59     int i,j;
60 
61     for(i=N-1;i>=1;i--)
62     {
63         for(j=1;j<=i;j++)
64         {
65             int max=(D[i+1][j]>D[i+1][j+1]?D[i+1][j]:D[i+1][j+1]);
66             D[i][j]=D[i][j]+max;
67         }
68     }
69     return D[1][1];
70 }
71 
72 int main(int argc, char *argv[])
73 {
74     int i,j;
75     freopen("001.in","r",stdin);
76     scanf("%d",&N);
77     for(i=1;i<=N;i++)
78     {
79         for(j=1;j<=i;j++)
80         {
81             scanf("%d",&D[i][j]);
82             maxSum[i][j]=-1;
83         }
84     }
85     
86     //printf("%d\n",fun1(1,1));
87     //printf("%d\n",fun2(1,1));
88     //printf("%d\n",fun3());
89     printf("%d\n",fun4());
90     return 0;
91 }

 補充一個深度優先搜索的實現。僅僅只是演示一下算法,時間復雜度應該是很高,無法AC的。

 1 #include<stdio.h>
 2 #define maxN 101
 3 int n,a[maxN][maxN]={0};
 4 int xx[2]={1,1};
 5 int yy[2]={0,1};
 6 int ans=0,maxAns=0;
 7 void dfs(int x,int y)
 8 {
 9     int i;
10     for(i=0;i<2;i++)
11     {
12         int newX=x+xx[i];
13         int newY=y+yy[i];
14         if(newX<=n&&newY<=n)
15         {
16             ans=ans+a[newX][newY];
17             if(newX==n)
18             {
19                 if(ans>maxAns) maxAns=ans;
20             }
21             else dfs(newX,newY);
22             ans=ans-a[newX][newY];
23         }
24     }
25 }
26 int main()
27 {
28     int i,j;
29     freopen("data.in","r",stdin);
30     scanf("%d",&n);
31     for(i=1;i<=n;i++)
32         for(j=1;j<=i;j++) scanf("%d",&a[i][j]);
33         
34     ans=maxAns=a[1][1];
35     dfs(1,1);
36     
37     printf("%d\n",maxAns);
38     return 0;
39 }
View Code

其實本質和上面遞歸實現的做法是一致的,只是代碼實現框架不同而已。

 

 

 

 

2193 數字三角形WW

題目鏈接:http://codevs.cn/problem/2193/

題目描述  Description

本道題目由上面數字三角形變化而來。具體變化是:從數字三角形頂部往底層走時必須經過某一個指定點,使之走的路程和最大

輸入描述  Input Description

第1行n,表示n行
第2到n+1行為每個的權值
程序必須經過n div 2,n div 2這個點

輸出描述  Output Description

最大值

樣例輸入  Sample Input

2
1
1 1

樣例輸出  Sample Output

2

數據范圍及提示  Data Size & Hint

n <=25

算法分析

這道題只需讓必須經過的點的權值加上一個特別大的值,最后的結果再減去這個值就行了。實際上,狀態轉移方程和上面第一題是沒有變的。

我這里在輸入時順便把所有元素累加求和得到sum。然后把這個sum加到必須走的那個點。

 1 #include <stdio.h>
 2 #define maxN 101
 3 int n,a[maxN][maxN];
 4 int main(int argc, char *argv[])
 5 {
 6     int i,j,x,y,sum=0;
 7     scanf("%d",&n);
 8     for(i=1;i<=n;i++)
 9         for(j=1;j<=i;j++)
10             { scanf("%d",&a[i][j]); sum=sum+a[i][j]; }
11     x=n/2; y=n/2;
12     a[x][y]+=sum;
13     for(i=n-1;i>=1;i--)
14     {
15         for(j=1;j<=i;j++)
16         {
17             a[i][j]+=(a[i+1][j]>a[i+1][j+1]?a[i+1][j]:a[i+1][j+1]);
18         }
19     }
20     printf("%d\n",a[1][1]-sum);
21     return 0;
22 }

 

2198 數字三角形WWW

題目鏈接:http://codevs.cn/problem/2198/

題目描述  Description

本道題目由上面第一題數字三角形變化而來。具體變化是:從數字三角形必須經過某一個點,使之走的路程和最大

輸入描述  Input Description

第1行n,表示n行 
第2到n+1行為每個的權值
第n+2行為兩個數x,y表示必須經過的點

輸出描述  Output Description

最大值

樣例輸入  Sample Input

2
1
1 1
1 1

樣例輸出  Sample Output

2

數據范圍及提示  Data Size & Hint

n<=25

算法分析

這個題目和第二題並沒什么區別,唯一區別就是輸入時多輸入x和y表示必須要走過的點。

 1 #include <stdio.h>
 2 #define maxN 101
 3 int n,a[maxN][maxN];
 4 int main(int argc, char *argv[])
 5 {
 6     int i,j,x,y,sum=0;
 7     scanf("%d",&n);
 8     for(i=1;i<=n;i++)
 9         for(j=1;j<=i;j++)
10             { scanf("%d",&a[i][j]); sum=sum+a[i][j]; }
11     //x=n/2; y=n/2;
12     scanf("%d%d",&x,&y);
13     a[x][y]+=sum;
14     for(i=n-1;i>=1;i--)
15     {
16         for(j=1;j<=i;j++)
17         {
18             a[i][j]+=(a[i+1][j]>a[i+1][j+1]?a[i+1][j]:a[i+1][j+1]);
19         }
20     }
21     printf("%d\n",a[1][1]-sum);
22     return 0;
23 }

 

上面三道題目的代碼中用到的動規狀態轉移方程都是逆推,從最后一行往前遞推。其實也是可以順着推的。比如第三題的代碼可以像下面這一段這么寫:(下面代碼中,f[i][j]表示的是從(i,j)這個點到達(1,1)這個點的最大路徑之和。另外,這里用了兩個數組,其實僅用一個數組即可,類似上述代碼。)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 using namespace std;
 5 int n,x,y,w[35][35],ko;
 6 long long f[35][35];
 7 int main()
 8 {
 9     scanf("%d",&n);
10     for(int i=1;i<=n;i++)
11       for(int j=1;j<=i;j++)
12         scanf("%d",&w[i][j]);
13     scanf("%d%d",&x,&y);
14     w[x][y]+=99999999;
15     f[1][1]=w[1][1];
16     for(int i=2;i<=n;i++)
17       for(int j=1;j<=i;j++)
18         f[i][j]=w[i][j]+max(f[i-1][j],f[i-1][j-1]);
19     long long ans=0;
20     for(int i=1;i<=n;i++)
21       ans=max(ans,f[n][i]);
22     printf("%lld",ans-99999999);
23     return 0;
24 }

 

2189 數字三角形W

題目鏈接:http://codevs.cn/problem/2189/

這個題目仍然是由上面第一題的數字三角形變化而來。具體如下:

題目描述  Description

數字三角形
要求走到最后mod 100最大

輸入描述  Input Description

第1行n,表示n行
第2到n+1行為每個的權值

輸出描述  Output Description

mod 100最大值

樣例輸入  Sample Input

2
1
99 98

樣例輸出  Sample Output

99

數據范圍及提示  Data Size & Hint

n<=25

算法分析

這個題目的改動使得問題處理起來比較麻煩了。本來比較大的一個數,加上那么一點點,再取模,可能就很小了。顯然這已經不再滿足動態規划的無后效性原則了。怎么辦呢?

可以開一個布爾型的三維數組,用f[i][j][k]表示走到位置(i,j)時路徑權值之和再取模能否得到k這個值,於是得到這樣一個狀態轉移方程:

f[i][j][k]=f[i][j][k] or f[i-1][j][(k-a[i][j]+m)%m] or f[i-1][j-1][(k-a[i][j]+m)%m],這里面加上m是為了防止出現負下標。
最后找一遍那個目標狀態存在就行了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int MAXN=26;
 6 int n;
 7 int a[MAXN][MAXN];
 8 bool f[MAXN][MAXN][101];
 9 int main()
10 {
11     scanf("%d",&n);
12     for(int i=1;i<=n;i++)
13         for(int j=1;j<=i;j++)
14             cin>>a[i][j];
15     
16     for(int i=1;i<=n;i++)f[n][i][a[n][i]%100]=1;
17     
18     for(int i=n-1;i>=1;i--)
19         for(int j=1;j<=i;j++)
20             for(int k=0;k<=99;k++)
21             f[i][j][k]=f[i+1][j][(k-a[i][j]+100)%100]||f[i+1][j+1][(k-a[i][j]+100)%100];
22     
23     for(int k=100;k>=1;k--)
24         if(f[1][1][k]==1){cout<<k;break;}
25     return 0;
26 }

下面這一段代碼可能比較好理解:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 using namespace std;
 5 int n,a[26][26]={0};
 6 bool f[26][26][102]={0};
 7 int main()
 8 {
 9     cin>>n;
10     for (int i=1;i<=n;i++)           //數據的讀入 
11         for (int j=1;j<=i;j++)
12             cin>>a[i][j];
13     
14     for (int i=1;i<=n;i++)          //這一步的代碼是為了將最后一排的所有數存下來 
15         f[n][i][a[n][i]]=true;
16     
17     for (int i=n-1;i>=1;--i)        //這里是主要過程,思路:將所有的可能性mod100都存下來, 
18         for (int j=1;j<=i;j++)
19             for (int k=0;k<=99;k++)
20                 {
21                     if (f[i+1][j][k])  f[i][j][(k+a[i][j])%100]=true;
22                     if (f[i+1][j+1][k])    f[i][j][(k+a[i][j])%100]=true;
23                 }
24         for (int k=99;k>=0;--k)       //找出所有可能性中最大的可能,輸出,這個題就AC咯、 
25             if (f[1][1][k])
26                 {
27                     cout<<k;
28                     return 0;
29                 }
30 }

 

4829 [DP]數字三角形升級版  &  4832 [DP]數字三角形升級版

(ps:兩道題其實是一樣的,據說數據規模不同)

題目鏈接:4829  ,  4832 

題目仍然是由上面第一題的數字三角形變化而來,具體如下:

題目描述  Description

從數字三角形的頂部到底部有很多條不同的路徑。對於每條路徑,把路徑上面的數加起來可以得到一個和,且你有一次機會可以把任何一個數重復加一遍

和最大的路徑稱為最佳路徑。你的任務就是求出最佳路徑上的數字之和。

輸入描述  Input Description

第一行:一個數,表示行數。

接下來n行為數塔

輸出描述  Output Description

一個數即最優結果

樣例輸入  Sample Input

5

1

1 3

1 1 3

1 1 1 3

7 1 1 1 3

樣例輸出  Sample Output

18

數據范圍及提示  Data Size & Hint

三角形行數不大於1000。最大和不大於maxlongint

算法分析

這個似乎有點類似於背包問題的回溯法求解。可以對動規數組增加一維,用於記錄從底層到達(i,j)這個位置的路徑上是否曾經有數字被重復使用過。具體參見以下代碼:

(代碼來自codevs討論版塊

 1 #include<iostream>
 2 using namespace std;
 3 int n;
 4 int a[1000][1000];
 5 int f[1000][1000][2];
 6 int i,j;
 7 int main()
 8 {
 9     cin>>n;
10     for(i=0;i<n;i++)
11       for(j=0;j<=i;j++)cin>>a[i][j];
12     for(i=0;i<n;i++){
13       f[n-1][i][0]=a[n-1][i];
14       f[n-1][i][1]=a[n-1][i]<<1;//f[n-1][i][1]=a[n-1][i]*2;
15       }
16     for(i=n-2;i>=0;i--)
17       for(j=0;j<=i;j++){
18         f[i][j][0]=a[i][j]+max(f[i+1][j][0],f[i+1][j+1][0]);
19         f[i][j][1]=a[i][j]+max(f[i+1][j][1],f[i+1][j+1][1]);
20         f[i][j][1]=max(f[i][j][1],f[i][j][0]+a[i][j]);
21         }
22     cout<<f[0][0][1]<<endl;
23     return 0;
24 }

 

再補充一道數字三角形變形的題目:

Vijos1006 晴天小豬歷險記之Hill      、    題解

 

關於數字三角形,還有一題:5585 數字三角形第k優解,暫時未搞懂怎解,希望各位大神留言解答呵呵。

 

參考資料:

【強烈建議閱讀】http://blog.csdn.net/Little_Flower_0/article/details/47945611

【強烈建議閱讀】http://blog.csdn.net/qq_35776409/article/details/62890528   

http://www.cnblogs.com/zwfymqz/p/6790960.html

http://www.genshuixue.com/i-cxy/p/8020923

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM