• 欢迎光临~

2022GDUT寒训专题二

开发技术 开发技术 2022-01-25 169次浏览

B题 最长公共子序列

题面

2022GDUT寒训专题二

样例

2022GDUT寒训专题二

思路

首先已知所求的是两个排列的最长公共子序列,如果二者有公共子序列,那么这个子序列的各个元素在P1出现的顺序一定和在P2出现的顺序是一样的。
我们实际上可以做的是以P1为基准,将P2中的元素与P1中的元素进行匹配。也就是说现在我们假设将P1数组存在一个离散化的数组中,数组的下标为1 2 3 4 5 6......然后我们现在做的其实是将找P2数组中的数存在与P1的哪个位置,就这样我们可以得到一个新的数组,这个数组是P2中的元素在P1中的排列顺序中对应的下标。
前面说了这个公共子序列出现的顺序一样,我们现在假设的是P1的下标为1 2 3 4 5 6......为升序,那么刚刚得到新的数组的元素也应该是升序。
由此问题就可以转化为新数组中的最长上升子序列问题

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
//这题是求两个排列的最长公共子序列。
//因为排列的特殊性,我们可以将问题转换为以P1顺序标号的P2的排序数组的最长上升子序列问题。
int _index[N];
int x[N];
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);cout.tie(NULL);
    //
    int n;cin >> n;
    for(int i = 1;i <= n;++i)
    {
        int temp;
        cin >> temp;
        //这个地方直接交换i和temp就行,因为i本身就是有序的。
        _index[temp] = i;
    }
    //将数组设为无穷大方便lower_bound操作
    //也就是免去了判断num是否大于最大值的操作
    memset(x, 0x3f3f3f, sizeof(x));
    for(int i = 1;i <= n;++i)
    {
        int temp;cin >> temp;
        //直接得到P2的下标
        int num = _index[temp];
        int pos = lower_bound(x+1, x+1+n, num)-x;
        x[pos] = num;
    }
    //
    int ans = 0;
    //如果走到了正无穷处,那他的前一位就是答案了
    for(int i = 1;i <= n+1;++i) if(x[i] > n) {ans = i-1;break;}
    cout << ans << endl;
    return 0;
}

E题 CD

题面

2022GDUT寒训专题二

题意

你有n个音乐片段和N个长度的时间,每段音乐都要播放一定长度的时间,现在问你如何刻录这n个音乐使得在不超过N的时限内刻录的音乐总时长最长。

样例

2022GDUT寒训专题二

思路

这题一眼就知道是01背包,但是主要是要得到路径,那么我们可以用01背包的二维dp形式,因为我们需要得到每一层所存储的值,根据状态转移方程逆推即可得到所选取的CD

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 30;
//这题就是01背包
int num[maxn];
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);cout.tie(NULL);
    //
    int N, n;
    while(cin >> N >> n)
    {
        for(int i = 1;i <= n;++i) cin >> num[i];
        vector<vector<int> >dp(N+1, vector<int>(N+1, 0));
        //
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= N;++j)
            {
                dp[i][j] = dp[i-1][j];
                if(j >= num[i]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-num[i]]+num[i]);
            }
        }
        //由上面的式子我们可以知道,如果选中了num[i]
        //那么恒有dp[i][t] - dp[i-1][t-num[i]] == num[i],其中t为当前体积
        //所以可以反推回原序列
        int t = dp[n][N];
        for(int i = n;i >= 1;--i)
        {
            if(dp[i][t] - dp[i-1][t-num[i]] == num[i])
            {
                cout << num[i] << " ";
                t -= num[i];
            }
        }
        cout << "sum:" << dp[n][N] << endl;
    }
    return 0;
}

G题 Flipping coins

题面

2022GDUT寒训专题二

题意

原先有n枚硬币反面朝上,现在你必须投掷K次硬币,每次投掷硬币可能会使得硬币变成正面或者反面,现在你想要让正面朝上的硬币数量最多,请输出投了K次硬币之后硬币正面朝上个数的期望。

思路

(等待更新中.....)

程序员灯塔
转载请注明原文链接:2022GDUT寒训专题二
喜欢 (0)
违法和不良信息举报电话:022-22558618 举报邮箱:dljd@tidljd.com