刷题记录(NC16664 [NOIP2004]合唱队形,NC235954 滑雪,NC235948 最大子串和,NC235624 牛可乐和最长公共子序列)

NC16664 [NOIP2004]合唱队形

题目链接

关键点:

1、题目要求求出连续的最长上升子序列+最长下降子序列

2、我们求出从左到右的每个点最长上升子序列,从右到左每个点的最长上升子序列

3、答案即为看每个位置的最长上升子序列长度+最长下降子序列长度的最大值

完整代码:

# include<bits/stdc++.h>
using namespace std;
int n, ans;
int a[110];
int dpr[110], dpl[110];
int main()
{
    cin>>n;
    for (int i=1; i<=n; i++)
    {
        cin>>a[i];
        dpr[i] = 1;
        dpl[i] = 1;
    }
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<i; j++)
        {
            if (a[i]>a[j])
                dpl[i] = max(dpl[i], dpl[j]+1);
        }
    }
    for (int i=n; i>=1; i--)
    {
        for (int j=n; j>i; j--)
        {
            if (a[i]>a[j])
                dpr[i] = max(dpr[i], dpr[j]+1);
        }
    }
    for (int i=1; i<=n; i++)
    {
//         cout<<dpl[i]<<" "<<dpr[i]<<endl;
        ans = max(ans, dpl[i]+dpr[i]-1);
    }
    cout<<n-ans<<endl;
    return 0;
}

NC235954 滑雪

题目链接

关键点:

1、对于每一个点的下滑长度,为其四周下滑长度+1的值取最大值

2、最好采用记忆化搜索,如果采用直接递推,不能按照数组的顺序去推,一个数的四周不一定在之前已经算出来。采用记忆化搜索,遇到没算出来的值,可以先调用函数先算出

完整代码:

# include <bits/stdc++.h>
using namespace std;
int n, m;
int g[110][110];
int dp[110][110];
int X[5] = {1, -1, 0, 0};
int Y[5] = {0, 0, 1, -1};
int find(int x, int y)
{
    if (dp[x][y])
        return dp[x][y];
    dp[x][y] = 1;
    for (int i=0; i<4; i++)
    {
        int x1 = X[i] + x;
        int y1 = Y[i] + y;
        if (x1<=0||y1<=0||x1>n||y1>m)
            continue;
        if (g[x][y] > g[x1][y1])
            dp[x][y] = max(dp[x][y], find(x1, y1)+1);
    }
    return dp[x][y];
}
int main()
{
    cin>>n>>m;
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=m; j++)
            cin>>g[i][j];
    }
    int ans = 0;
    for (int i=1; i<=n; i++)
    {
        for (int j=1; j<=m; j++)
        {
//             cout<<i<<" "<<j<<" "<<find(i, j)<<endl;
            ans = max(ans, find(i, j));
        }
    }
    cout<<ans<<endl;
    
    
    
    return 0;
}

NC235948 最大子串和

题目链接

关键点:

1、对于一个数组,对于每一位,我们都算出选择该数的最大值,那么就有就和是否选择之前的数进行比较

dp[i] = max(dp[i-1]+a[i], a[i]);表示选择当前数的最大值(一定选择当前数,看是否选择之前的数)

完整代码:

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
ll a[1000000+10];
ll dp[1000000+10];
ll ans;
int main()
{
    cin>>n;
    for (int i=1; i<=n; i++)
        cin>>a[i];
    for (int i=1; i<=n; i++)
    {
        dp[i] = max(a[i]+dp[i-1], a[i]);
    }
    
    for (int i=1; i<=n; i++)
    {
       ans = max(ans, dp[i]); 
    }
    cout<<ans<<endl;
    return 0;
}

NC235624 牛可乐和最长公共子序列

题目链接

关键点:

1、注意该题有多组数据!

2、对于两个序列,我们取两个序列的结尾来推

3、dp[i][j];表示第一个序列到i,但不包括i,第二个序列到j ,但不包括j

那么比较i前一个字母和j的前一个字母看是否相同,相同则为之前的+1,即

dp[i][j] = dp[i-1][j-1]+1;

该式子也表明了我们为什么不将动态转移方程设为包括i, j,如果从0开始遍历,数组下标会越界

4、如果i-1字母和j-1不相同,那么就在两个串分别减少一个长度中取最大值

dp[i][j] = max(dp[i][j-1], dp[i-1][j]);

5、因为该方程是从头一点点遍历到尾,所以最终答案即为dp[s1.size()][s2.size()];

完整代码:

# include <bits/stdc++.h>
using namespace std;
string s1, s2;
int dp[5010][5010];
int main()
{
    while(cin>>s1>>s2)
    {
//         memset(dp, 0, sizeof(dp));
        for (int i=1; i<=s1.size(); i++)
        {
            for (int j=1; j<=s2.size(); j++)
            {
                if (s1[i-1] == s2[j-1])
                    dp[i][j] = dp[i-1][j-1]+1;
                else
                    dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
            }
        }
        cout<<dp[s1.size()][s2.size()]<<endl;
    }
    
    
    
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值