0
点赞
收藏
分享

微信扫一扫

【Algorithm】动态规划(一)

肉肉七七 2022-03-23 阅读 61

一、数字三角形

Problem

Solution

 Code

#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];
    
    // 从第 n - 1 层不断往上计算出 dp[i][j]
    for(int i = n - 1; i >= 0; 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];

    return 0;
}

二、过河卒

Problem

Solution

如果没有🐎的话,C(n, m+n) 种方法:

 但换到有马阻拦的问题中,单纯地这样搜索就行不通了

这张图所得的答案虽然是正确的,但实际上这样的操作是错误的,图中蓝色的“1”应该改为0。

 因为这个位置后面被马头挡住,自然是行不通的,值应为0,在这个例子中这里的值是1还是0对答案没有影响,但大家可以想象,如果在最左边一条边上,一个点上下都是马能走到的位置,值还为1的话,就会影响它右侧点的值。

 就像上图,正常情况下红色点所在的一整列初始赋值都是1,但是红色点实际值应该为0,如果值仍赋为1,则会导致蓝色点的值比实际值大1,从而导致整个结果错误。因此,我们在赋初值时,要专门考虑最上和最左一列的情况。

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long

int n, m, x, y;
int dp[30][30], mp[30][30];

signed main()
{
    cin >> n >> m >> x >> y;

    mp[x-1][y-2] = 1;
    mp[x-2][y-1] = 1;
    mp[x-1][y+2] = 1;
    mp[x-2][y+1] = 1;
    mp[x+1][y-2] = 1;
    mp[x+1][y+2] = 1; 
    mp[x+2][y-1] = 1; 
    mp[x+2][y+1] = 1;
    mp[x][y] = 1;

    for(int i = 0; i <= n; i++) {
        if(mp[i][0]) break;
        dp[i][0] = 1;
    }

    for(int i = 0; i <= m; i++) {
        if(mp[0][i]) break;
        dp[0][i] = 1;
    }

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            if(mp[i][j]) dp[i][j] = 0;
            else dp[i][j] = dp[i][j-1] + dp[i-1][j];
        }

    // dp[0][0] = 1;
    // for(int i = 0; i <= n; i++) {
    //     for(int j = 0; j <= m; j++) {
    //         if(!mp[i+1][j]) dp[i+1][j] += dp[i][j];
    //         if(!mp[i][j+1]) dp[i][j+1] += dp[i][j];
    //     }
    // }
            
//     for(int i = 0; i <= n; i++) {
//         for(int j = 0; j <= m; j++) {
//             cout << dp[i][j] << " ";
//         }
//         cout << "\n";
//     }

    cout << dp[n][m] << "\n";
}

三、传球游戏

Problem

Solution

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long

int dp[40][40];

signed main()
{
    int n, m;
    cin >> n >> m;

    dp[0][1] = 1;

    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++) {
            if(j == 1)
                dp[i][j] = dp[i-1][n] + dp[i-1][2];
            else if(j == n)
                dp[i][j] = dp[i-1][1] + dp[i-1][n-1];
            else 
                dp[i][j] = dp[i-1][j-1] + dp[i-1][j+1];
        }
    
    cout << dp[m][1];
}

四、最长不下降子序列(LIS)

Problem

 Solution

dp[ i ] 表示以 a[ i ] 结尾的最长不下降子序列长度。这样对 a[ i ] 来说有两种可能:

1、如果存在 a[ i ] 之前的元素 a[ j ]( j < i),使得 a[ j ] <= a[ i ] 且 dp[ j ] + 1 > dp[ i ](即把 a[ i ] 跟在以 a[ j ] 结尾的 LIS 后面时能比当前以 a[ i ] 结尾的 LIS 长度更长),那么就把 a[ i ] 跟在以 a[ j ] 结尾的 LIS 后面,形成一条更长的不下降子序列(令 dp[ i ] = dp[ j ] + 1)。

2、如果 a[ i ] 之前的元素都比 a[ i ] 大,那么 a[ i ] 就只好自己形成一条 LIS,但是长度为 1,即这个子序列里面只有一个 a[ i ]。

 

 Code

#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 res = -1;   // 记录最大的 dp[i]
    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;
        }
        res = max(res, dp[i]);
    }

    cout << res << "\n";

    return 0;
}

五、滑雪

Problem

Solution

 Code

以下是错误代码,以为要算 dp[ i ][ j ] 的时候,要知道上下左右的四个值。解决方案,按从小到大算 dp,但太过麻烦。我们可以试试记忆化搜索。

#include <bits/stdc++.h>
using namespace std;

const int N = 100;
int dp[N][N], f[N][N];

int main()
{
    int n, m;
    cin >> n >> m;

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++) {
            cin >> f[i][j];
            dp[i][j] = 1;
        }
    
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            int a, b, c, d;
            a = b = c = d = 1;
            if(f[i-1][j] < f[i][j])
                a = f[i-1][j] + 1;
            if(f[i+1][j] < f[i][j])
                b = f[i+1][j] + 1;
            if(f[i][j-1] < f[i][j])
                c = f[i][j-1] + 1;
            if(f[i][j+1] < f[i][j])
                d = f[i][j+1] + 1;
            dp[i][j] = max(max(a, b), max(c, d));
        }
    }

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            cout << dp[i][j] << " ";
        }
        cout << "\n";
    }

    return 0;
}
//什么是记忆化搜索?
//简单的说就是深搜或者广搜有时会对一个数据多次没必要的访问,我们用一个数组把这个数据的值保存下来,比这个数值小或者大的时间我们就没有必要再继续访问。
 
#include<iostream>
using namespace std;
int to[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int n, m;
int high[105][105];
int maxLen[105][105];
bool check(int x, int y){
	if(x >= 1 && y >= 1 && x <= n && y <= m)
		return 1;
	else
		return 0;
}
 
int dfs(int x, int y){
	if (maxLen[x][y] != 0)	//退出边界 ,这个点已经搜索过,或者这个点是边界
		return maxLen[x][y];
	
	maxLen[x][y] = 1;        //最低的点就是1;
	for(int i = 0; i <= 4; i++){    //搜索上下左右
		int x1 = x + to[i][0];
		int y1 = y + to[i][1];
		if( check(x1, y1) && high[x1][y1] < high[x][y]){    //判断能否向下滑
			maxLen[x][y] = max(dfs(x1, y1) + 1, maxLen[x][y]);
		}
	}
	return maxLen[x][y];    //返回搜索过的点,并保存在数组中
}
int main(){
 
	cin >> n >> m;
 
	int ans = 1;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			cin >> high[i][j];
			maxLen[i][j] = 0;      
		}
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			ans = max(ans, dfs(i, j));	//搜索每一个点,并且找出最大值
		}
	}
	cout << ans << endl;
	return 0;
} 

六、最大连续子序列和

Problem

Solution

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 10010;

int a[maxn], dp[maxn];
// a[i] 存放序列, dp[maxn] 存放以 a[i] 结尾的连续子序列和

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] << "\n";

    return 0;
}
举报

相关推荐

0 条评论