这次我换一种方式,先给出例题再归纳算法方法
例题一、数字三角形
递归代码如下:
#include<stdio.h>
#define MAX 101
int max(int a, int b)
{
return a > b ? a : b;
}
int D[MAX][MAX];
int n;
int MaxSum(int i, int j)
{
if(i == n)
return D[i][j];
int x = MaxSum(i + 1, j);
int y = MaxSum(i + 1, j + 1);
return max(x, y) + D[i][j];
}
int main()
{
int i, j;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
scanf("%d", &D[i][j]);
}
}
printf("%d\n", MaxSum(1, 1));
return 0;
}
为什么超时?
改进策略:
优化版:记忆递归型动归程序
#include<stdio.h>
#define Max 101
int max(int a, int b)
{
return a > b ? a : b;
}
int D[Max][Max];
int n;
int maxSum[Max][Max]; //用来存放每一个数字到底边的最大和
int MaxSum(int i, int j)
{
if(maxSum[i][j] != -1)
return maxSum[i][j];
if(i == n)
maxSum[i][j] = D[i][j];
else
{
int x = MaxSum(i + 1, j);
int y = MaxSum(i + 1, j + 1);
maxSum[i][j] = max(x, y) + D[i][j];
}
return maxSum[i][j];
}
int main()
{
int i, j;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
scanf("%d", &D[i][j]);
maxSum[i][j] = -1;
}
}
printf("%d\n", MaxSum(1, 1));
return 0;
}
递推方案:
详细C代码如下:
#include<stdio.h>
#define Max 101
int D[Max][Max];
int maxsum[Max][Max] = {0};
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int i, j;
int n;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
scanf("%d", &D[i][j]);
}
}
for(i = 0; i <= n; i++)
{
maxsum[n][i] = D[n][i];
}
for(i = n - 1; i >= 1; i--)
{
for(j = 1; j <= i; j++)
maxsum[i][j] = D[i][j] + max(maxsum[i + 1][j], maxsum[i + 1][j + 1]);
}
printf("%d\n", maxsum[1][1]);
return 0;
}
我们可以把空间优化一下,把maxsum转化为一维数组,节省空间
#include<stdio.h>
#define Max 101
int max(int a, int b)
{
return a > b ? a : b;
}
int D[Max][Max];
int maxsum[Max] = {0};
int main()
{
int i, j;
int n;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
scanf("%d", &D[i][j]);
}
}
for(i = 1; i <= n; i++)
maxsum[i] = D[n][i];
for(i = n - 1; i >= 1; i--)
{
for(j = 1; j <= i; j++)
{
maxsum[j] = D[i][j] + max(maxsum[j], maxsum[j + 1]);
}
}
printf("%d\n", maxsum[1]);
return 0;
}
这个题还可以进一步空间优化,指针法
让指针maxsum指向D[n],把D[n]的首地址给maxsum,maxsum[1]就是D[n][1],依次类推
#include<stdio.h>
#define Max 101
int D[Max][Max];
int n;
int *maxsum;
int max(int a, int b)
{
return a > b ? a : b;
}
int main()
{
int i, j;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
scanf("%d", &D[i][j]);
}
}
maxsum = D[n];
for(i = n - 1; i >= 1; i--)
{
for(j = 1; j <= i; j++)
{
maxsum[j] = D[i][j] + max(maxsum[j], maxsum[j + 1]);
}
}
printf("%d\n", maxsum[1]);
return 0;
}
动归思路:
动归的应用场景:
例题二、最长上升子序列
也就是说通过a数组承接输入的N个数据,maxLen数组记载以第i个数为最右端的最大子序列,相应得出最大子序列
详细C代码如下:
#include<stdio.h>
#define Max 1010
int a[Max];
int maxLen[Max];
#define max(a, b) (a > b ? a : b)
void sort(int a[], int n)
{
int i, j;
int t = 0;
for(i = 1; i < n; i++)
{
for(j = i + 1; j < n + 1; j++)
{
if(a[i] > a[j])
{
t = a[j];
a[j] = a[i];
a[i] = t;
}
}
}
}
int main()
{
int N;
scanf("%d", &N);
int i, j;
for(i = 1; i <= N; i++)
{
scanf("%d", &a[i]);
maxLen[i] = 1;
}
for(i = 2; i <= N; i++)
{
for(j = 1; j < i; j++)
{
if(a[i] > a[j])
maxLen[i] = max(maxLen[i], maxLen[j] + 1);
}
}
sort(maxLen, N);
printf("%d\n", maxLen[N]);
return 0;
}
部分运行结果如下:
关于动态规划,蓝桥杯还有很多相关题目,可以参考该博文蓝桥杯2018第九届C语言B组省赛习题题解——习题D.测试次数*(经典递推)
例题三、最长公共子序列***
思路:
详细C代码如下:
#include<stdio.h>
#include<string.h>
char s1[1000];
char s2[1000];
int maxLen[1000][1000];
#define max(a, b) (a > b ? a : b)
int main()
{
gets(s1);
gets(s2);
int len1 = strlen(s1);
int len2 = strlen(s2);
int i, j;
for(i = 0; i <= len1; i++)
{
maxLen[i][0] = 0;
}
for(j = 0; j <= len2; j++)
{
maxLen[0][j] = 0;
}
for(i = 1; i <= len1; i++)
{
for(j = 1; j <= len2; j++)
{
if(s1[i - 1] == s2[j - 1])
{
maxLen[i][j] = maxLen[i - 1][j - 1] + 1;
}
else
{
maxLen[i][j] = max(maxLen[i - 1][j], maxLen[i][j - 1]);
}
}
}
printf("%d\n", maxLen[len1][len2]);
return 0;
}
部分运行结果如下:
例题四、神奇的口袋
递归法:
#include<stdio.h>
int a[30];
int N;
int Ways(int w, int k) //w为需要凑的体积
{
if(w == 0)
return 1;
if(k <= 0) //没有物品可选
return 0;
return Ways(w, k - 1) + Ways(w - a[k], k - 1);
} //不选第k种物品和选第k种物品
int main()
{
scanf("%d", &N);
int i;
for(i = 1; i <= N; i++)
{
scanf("%d", &a[i]);
}
printf("%d\n", Ways(40, N));
return 0;
}
样例运行结果如下:
动归解法:
#include<stdio.h>
#include<string.h>
int main()
{
int N;
scanf("%d", &N);
int ans[41][N + 1];
int a[N + 1];
memset(ans, 0, sizeof(ans));
int i, j;
for(j = 1; j <= N; j++)
{
scanf("%d", &a[j]);
ans[0][j] = 1;
}
ans[0][0] = 1;
int v;
for(v = 1; v <= 40; v++)
{
for(j = 1; j <= N; j++)
{
ans[v][j] = ans[v][j - 1];
if(v - a[j] >= 0)
ans[v][j] += ans[v - a[j]][j - 1];
}
}
printf("%d\n", ans[40][N]);
return 0;
}
部分运行结果如下
斐波那契数列记忆化搜索
#include<bits/stdc++.h>
using namespace std;
#define maxn 100
int dp[maxn];
int F(int n)
{
if(n == 0 || n == 1)
{
return 1;
}
if(dp[n] != -1)
{
return dp[n];
}
else
{
dp[n] = F(n - 1) + F(n - 2);
return dp[n];
}
}
int main()
{
memset(dp, -1, sizeof(int) * maxn);
cout << F(5) << endl;
return 0;
}
运行结果:
数塔
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000;
int f[maxn][maxn], dp[maxn][maxn];
int main()
{
int n;
cin >> n;
//输入数塔
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= i; j++)
{
cin >> f[i][j];
}
}
//底层边界
for(int j = 1; j <= n; j++)
{
dp[n][j] = f[n][j];
}
for(int i = n - 1; i >= 1; i--)
{
for(int j = 1;j <= i; j++)
{
dp[i][j] = max(dp[i + 1][j], dp[i + 1][j + 1]) + f[i][j];
}
}
cout << dp[1][1] << endl;
return 0;
}
样例运行结果:
最大连续子序列和
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int A[maxn], dp[maxn];
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
cin >> A[i];
}
dp[0] = A[0];
for(int i = 1; i < n; i++)
{
dp[i] = max(A[i], dp[i - 1] + A[i]);
}
int k = 0;
for(int i = 1; i < n; i++)
{
if(dp[i] > dp[k])
{
k = i;
}
}
cout << dp[k] << endl;
return 0;
}
样例运行结果:
最长不下降子序列(LIS)
#include<bits/stdc++.h>
using namespace std;
const int N = 100;
int A[N], dp[N];
int main()
{
int n;
cin >> n;
for(int i = 1; i <= n; i++)
{
cin >> A[i];
}
int ans = -1;
for(int i = 1; i <= n; i++)
{
dp[i] = 1;
for(int j = 1; j < i; j++)
{
if(A[i] >= A[j] && (dp[j] + 1 > dp[i]))
{
dp[i] = dp[j] + 1;
}
}
ans = max(ans, dp[i]);
}
cout << ans << endl;
return 0;
}
样例运行结果: