今天有三节课,但是没事,可以翘。
今天有算法分析作业。
[TOC]
record一下:
动态规划
为什么使用递归求斐波纳契数列很慢? 复杂度是$2^n$(大概),多个重叠子问题,重复计算。使用空间换时间,避免重复计算。
我算fib(5) -> fib(3) + fib(4) -> fib(1)+fib(2) + fib(2)+fib(3)
Basic idea
保存子问题的结果,避免重复计算。
用额外的数据保存(备忘录memo)
空间换时间
大问题可以由子问题推出(状态转移)我个人感觉很像分治
1 2 3 4 5 6 int memo[10005 ];int fib (int n) { if (memo[n]!=0 )return memo[n]; if (n==1 ||n==2 )return memo[n]=1 ; return memo[n]=fib (n-1 )+fib (n-2 ); }
P1216
递归: $d(r,j)$表示第r行,第j个数(r,j从1开始)
maxSum(r,j)
:表示从(r,j)位置到底表最优路线和。 $$ maxSum(r,j)=\begin{cases} d(r,j)&r=n\ Max(maxSum(r+1,j),maxSum(r+1,j+1))+d(r,j)&r\ne n\ \end{cases} $$
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const int MAXN=1005 ;int d[MAXN][MAXN];int n;int maxSum (int r,int j) { if (r==n)return d[r][j]; int x=maxSum (r+1 ,j); int y=maxSum (r+1 ,j+1 ); return max (x,y)+d[r][j]; } int main () { cin>>n; for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=i;j++){cin>>d[r][j];} } cout<<maxSum (1 ,1 )<<endl; }
使用MEMO:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 int memo[MAXN][MAXN];int maxSum (int r,int j) { if (memo[r][j]!=-1 )return memo[r][j]; if (r==n)return memo[r][j]=d[r][j]; int x=maxSum (r+1 ,j); int y=maxSum (r+1 ,j+1 ); return memo[r][j]=max (x,y)+d[r][j]; } int main () { cin>>n; memset (memo,-1 ,sizeof (memo)); for (int i=1 ;i<=n;i++){ for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=i;j++){cin>>d[i][j];} } } cout<<maxSum (1 ,1 )<<endl; return 0 ; }
上面的这种方法是自顶向下的:在main函数内是maxSum(1,1)
从第一行第一个元素开始。
我们也可以选择从下往上填MEMO。
递推+填表
7
|
30
3
8
|
23
21
8
1
0
|
20
13
10
2
7
4
4
|
7=max(4,5)+2
12
10
10
4
5
2
6
5
|
4
5
2
6
5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int memo[MAXN][MAXN];int d[MAXN][MAXN];int main () { cin>>n; for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=i;j++){cin>>d[i][j];} } for (int i=1 ;i<=n;i++){memo[n][i]=d[n][i];} for (int i=n-1 ;i;i--){ for (int j=1 ;j<=i;j++){ memo[i][j]=max (memo[i+1 ][j],memo[i+1 ][j+1 ])+d[i][j]; } } cout<<memo[1 ][1 ]<<endl; }
可以看到,上面会使用到二维数组。但是我们实际上只填了一半,比较浪费。可以优化成一维数组。先可以把memo的二维数组变成一维(一行n)。
basic idea: 主要是求完memo[r][j]后memo[r+1][j]就不再使用,因此可以用memo[r][j]覆盖memo[r+1][j]。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int memo[MAXN];int main () { cin>>n; for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=i;j++){cin>>d[i][j];} } for (int i=1 ;i<=n;i++){memo[i]=d[n][i];} for (int i=n-1 ;i;i--){ for (int j=i;j<=i;j++){ memo[j]=max (memo[j],memo[j+1 ])+d[i][j]; } } cout<<memo[1 ]<<endl; }
P2 训练计划 这题乍看是个DP,但是实际上应该是计算图以及最短路。(感觉并查集就好了)
1 2 3 4 5 6 7 8 9 10 11 12 13 void input () { int course[105 ][2 ]; int n,m; cin>>n>>m; for (int i=1 ;i<=m;i++){ cin>>course[i][0 ]; if (!course[i][0 ])course[i][0 ]=i; } for (int i=1 ;i<=m;i++){ cin>>course[i][1 ]; } }
计算每科的最早开始时间;计算完之后计算maxTime>n时,就不往后计算了。否则就要逆推得到每科的最晚开始时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int find (int i) { if (earliest_time[i]!=-1 )return earliest_time[i]; if (course[i][0 ]==i)return earliest_time[i]=1 ; int father=course[i][0 ]; return earliest_time[i]=find (a)+course[a][1 ]; } int main () { iuput (); int earliest_time[m]; memset (earliest_time,-1 ,sizeof (earliest_time)); for (int i=1 ;i<=m;i++){ cout<<find (i)<<" " ; } }
测试是否会超过给的时间:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 for (int i=1 ;i<=m;i++){ if (earliest_time[i]+course[i][1 ]>n)return 0 ; } int latest_time[105 ];int find_late (int i) { if (latest_time[i]!=-1 )return latest_time[i]; if (course[i][0 ]==i)return latest_time[i]=(n+1 -course[i][1 ]); int fa=course[i][0 ]; return latest_time[i]=find_late (fa)-course[i][1 ]; }
算法分析作业 每个子问题的规模都一样,且相对于父问题都是成比例缩小。 $$ T(n)=aT(\frac n 2)+f(n) $$