一、作业题:挖地雷
在一个地图上有n个地窖(n≤200),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径,并规定路径都是单向的,且保证都是小序号地窖指向大序号地窖,也不存在可以从一个地窖出发经过若干地窖后又回到原来地窖的路径。某人可以从任意一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使他能挖到最多的地雷。
输入格式:
第一行:地窖的个数;
第二行:为依次每个地窖地雷的个数;
下面若干行:
xi yi //表示从xi可到yi,xi<yi。
最后一行为"0 0"表示结束。
输出格式:
k1-k2−…−kv //挖地雷的顺序 挖到最多的雷。
△通过看邻接表来确认两地窖是否通路△
参考代码如下:
1 #include<iostream> 2 using namespace std; 3 4 bool b[201][201];//判断地窖是否有地雷 5 int f[201],w[201],a[201]; 6 int i,j,n,l,k,x,y,ans; 7 int main() 8 { 9 cin>>n; 10 for(i=1;i<=n;i++) 11 cin>>w[i]; 12 do 13 { 14 cin>>x>>y; 15 if(x!=0&&y!=0) b[x][y]=true; 16 } 17 while(x!=0||y!=0); 18 f[n]=w[n]; 19 for(i=n-1;i>=1;i--) 20 {//从下往上填表 21 l=0,k=0; 22 for(j=i+1;j<=n;j++) 23 if(b[i][j]&&f[j]>l) l=f[j],k=j; 24 f[i]=l+w[i]; 25 a[i]=k; 26 } 27 l = k =0;//重新赋值 28 for(i=1;i<=n;i++) 29 if(f[i]>k) k=f[i],l=i; 30 ans=k; 31 cout<<l;//输出起点 32 while(a[l]) 33 { 34 cout<<"-"<<a[l]; 35 l=a[l]; 36 } 37 cout<<endl; 38 cout<<ans<<endl;//输出最优结果 39 return 0; 40 }
1.1 根据最优子结构性质,列出递归方程式——>m[i] = max{m[j] +a[i]}
ps.其中(i,j)∈E意味着两条边能通路,a[i]表示出发的点的本身地雷数,m[i]代表从地窖i出发挖到的最多地雷数!
1.2 给出填表法中表的维度、填表范围和填表顺序:
①维度:一维;②填表范围:i<j<=n,(i,j)∈E;③填表顺序:自下往上
1.3 分析该算法的时间和空间复杂度:
①时间复杂度: 通过循环,得到时间复杂度为O(n²);
②空间复杂度: 由于需要申请二维数组,则其空间复杂度也为O(n²);
二、对动态规划的理解
仔细学下来,慢慢发现动态规划法其实细节本质上还是分而治之的方法。我们在作业题中常常需要将题目情景划分成许多个子问题的方式来解决。例如挖地雷这道题,①号地窖与②号和④号相连,于是在分析①的时候,可以分成两个子问题:
于是动态规划在算法实现中会避免很多重复计算子问题的情况,转而由独立计算并记录好的子问题结果求出大问题的解,大大提高了算法的效率。
同时在动态规划的学习中,对填表法的理解清晰了很多,觉得在敲代码前的对题目的分析会减少在做题中的逻辑失误。
课本所示动态规划算法的步骤大概分成四步:
(1)找出最优解的性质,并刻画其结构特征
(2)递归地定义最优值
(3)以自底向上的方式计算最优值(相当于我们的填表法顺序)
(4)根据计算最优值时得到的信息构造最优解
三、结对编程的情况
结对编程中的确不仅培养了合作敲代码的能力,更增加了与队友的沟通机会。刚开始可能由于自己的递归方程的理解失误,所以导致一直有错误的测试点。之后通过讨论,了解了正确的递归思路,敲出了一份答案正确的代码。但我和搭档在解释代码思路和逻辑的过程中,她提出的一些问题让我对自己的逻辑也产生了疑惑,于是我们整体还是比较迷糊的。最后通过老师对我们“边界情况”部分代码的讲解我们才恍然大悟,有些时候搭档也会帮你发现自己无法发现的错误点,所以结对编程对自己的成长有着一定的作用~下次的学习中也会更加努力学会沟通与合作!