luogu P1216 (USACO1.5) Number Triangles

这题是一道很好的DP入门练手题

但是总有一些同学想到了贪心
所以让我来展示一下33分的贪心

 1 #include <bits/stdc++.h>
 2 #define fp(i,l,r) for(register int i=(l);i<=(r);i++)
 3 #define fd(i,r,l) for(register int i=(r);i>=(l);i--)
 4 using namespace std;
 5 int _ans=0;
 6 int n;
 7 int a[1000][1000];
 8 int _count=0;
 9 inline int _max(int a,int b){
10     if(a>b){
11         return a;
12     }
13     return b;
14 }
15 int yh13(int x,int y){
16     if(x==n){
17         return 0;
18     }
19     _ans+=_max(a[x+1][y],a[x+1][y+1]);
20     if(_max(a[x+1][y],a[x+1][y+1])==a[x+1][y]){
21         yh13(x+1,y);
22     }
23     else{
24         yh13(x+1,y+1);
25     }
26 }
27 int main(){
28     scanf("%d",&n);
29     fp(i,1,n){
30         fp(j,1,i){
31             scanf("%d",&a[i][j]);
32         }
33     }
34     _ans=a[1][1];
35     yh13(1,1);
36     printf("%d",_ans);
37     return 0;
38 }
39 //代码贼丑别吐槽
33分贪心

为什么会得33分呢?
让我们来分析一下
我们题目中的数据是这样的


5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5


按照的贪心的路径就应该是(1,1)->(2,2)->(3,2)->(4,2)->(5,2) 答案是28
没毛病,对不对?
错!
如果我们仔细观察,就会发现有(1,1)->(2,1)->(3,1)->(4,2)->(5,2)得到答案30
这是怎么回事呢?
这证明贪心是不可行的,因为这道题不具备贪心选择性质


听说搜索特别万能?那我试试看

 1 #include <bits/stdc++.h>
 2 #define fp(i,l,r) for(register int i=(l);i<=(r);i++)
 3 #define fd(i,r,l) for(register int i=(r);i>=(l);i--)
 4 using namespace std;
 5 int _ans=0;
 6 int n;
 7 int a[1000][1000];
 8 int _count=0;
 9 inline int _max(int a,int b){
10     if(a>b){
11         return a;
12     }
13     return b;
14 }
15 inline int search(int x,int y){
16     _count+=a[x][y];
17     if(x==n){
18         _ans=_max(_ans,_count);
19     }
20     else{
21         search(x+1,y);
22         search(x+1,y+1);
23     }
24     _count-=a[x][y];
25 }
26 int main(){
27     scanf("%d",&n);
28     fp(i,1,n){
29         fp(j,1,i){
30             scanf("%d",&a[i][j]);
31         }
32     }
33     search(1,1);
34     printf("%d",_ans);
35     return 0;
36 }//更丑了
55分搜索

从33分变成了55分 RE了
 想必问题已经很明显了吧,这个程序搜索了重复的数字,所以我们可以据此加一个记忆化

 1 #include <bits/stdc++.h>
 2 #define fp(i,l,r) for(register int i=(l);i<=(r);i++)
 3 #define fd(i,r,l) for(register int i=(r);i>=(l);i--)
 4 using namespace std;
 5 int n;
 6 int a[1000][1000];
 7 int f[1000][1000]; 
 8 int _count=0;
 9 inline int _max(int a,int b){
10     if(a>b){
11         return a;
12     }
13     return b;
14 }
15 inline int search(int x,int y){
16     int _ans=0;
17     if(x==n){
18         return a[x][y];
19     }
20     if(f[x+1][y]==-1){
21         f[x+1][y]=search(x+1,y);
22     }
23     if(f[x+1][y+1]==-1){
24         f[x+1][y+1]=search(x+1,y+1);
25     }
26     _ans=_max(f[x+1][y],f[x+1][y+1])+a[x][y];
27     return _ans;
28 }
29 int main(){
30     scanf("%d",&n);
31     fp(i,1,n){
32         fp(j,1,i){
33             scanf("%d",&a[i][j]);
34             f[i][j]=-1;
35         }
36     }
37     printf("%d",search(1,1));
38     return 0;
39 }//啊啊啊啊啊啊啊
满分记忆化搜索

这回没毛病了100分
 时间复杂度从搜索的O(2^n)变成了O(n^2)


其实还有一个和搜索差不多的做法(这个在luogu博客我没有发上去了),用分治。

设定两个变量用以递归求得最优子结构,时间复杂度O(2^n)

 1 #include <bits/stdc++.h>
 2 #define fp(i,l,r) for(register int i=(l);i<=(r);i++)
 3 #define fd(i,r,l) for(register int i=(r);i>=(l);i--)
 4 using namespace std;
 5 int ans=0;
 6 int n;
 7 int a[1000][1000];
 8 inline int _max(int a,int b){
 9     if(a>b){
10         return a;
11     }
12     return b;
13 }
14 inline int fz(int x,int y){
15     if(x==n){
16         return a[x][y];
17     }
18     int s1=fz(x+1,y);
19     int s2=fz(x+1,y+1);
20     return _max(s1,s2)+a[x][y];
21 }
22 int main(){
23     scanf("%d",&n);
24     fp(i,1,n){
25         fp(j,1,i){
26             scanf("%d",&a[i][j]);
27         }
28     }
29     printf("%d",fz(1,1));
30     return 0;
31 }//这个是最丑的
55分分治

可是你再看看这篇题解的第一句话这题是一道很好的DP入门练手题
有没有感觉被欺骗了?
我还是把DP的思路及代码交上来吧
这题从上往下推就需要有其他的判断了
 所以我选择了从下往上**递推**
 这样就符合了最优子结构性质了
状态转移方程 f[x][y]=max(f[x+1][y],f[x+1][y+1])+a[x][y];(其实不用多讲把)
最后献上代码

 1 #include <bits/stdc++.h>
 2 #define fp(i,l,r) for(register int i=(l);i<=(r);i++)
 3 #define fd(i,r,l) for(register int i=(r);i>=(l);i--)
 4 using namespace std;
 5 inline int _max(int a,int b){
 6     if(a>b){
 7         return a;
 8     }
 9     else{
10         return b;
11     }
12 }
13 int a[1000][1000],f[1000][1000];
14 int main(){
15     int n;
16     scanf("%d",&n);
17     fp(i,1,n){
18         fp(j,1,i){
19             scanf("%d",&a[i][j]);
20             f[i][j]=a[i][j];
21         }
22     }
23     fd(i,n-1,1){
24         fp(j,1,i){
25             f[i][j]=_max(f[i+1][j],f[i+1][j+1])+a[i][j];
26         }
27     }
28     printf("%d",f[1][1]);
29     return 0;
30 }//...
满分动态规划

完美收场

That's all.

原文地址:https://www.cnblogs.com/Fraction/p/8166402.html