第三周训练题单

数字三角形

#include <bits/stdc++.h>

using namespace std;

#define int long long


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    while( cin >> n){
        for( int i = 1 ; i <= n ; i ++ ){
            for( int j = 1 ; j <= i ; j ++ )
                cout << j << " ";
            cout <<"\n";
        }
    }
    return 0;
}

走楼梯

#include <bits/stdc++.h>

using namespace std;

#define int long long


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> f(n+1);
    f[0] = f[1] = 1;
    for( int i = 2 ; i <= n ; i ++ )
        f[i] = f[i-1] + f[i-2];
    cout << f[n] << "\n";
    return 0;
}

最大子串和

#include <bits/stdc++.h>

using namespace std;

#define int long long


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n , res = 0;
    cin >> n;
    for( int i = 1 , x , cnt = 0; i <= n ; i ++ ){
        cin >> x;
        cnt = max( 0ll , cnt + x );
        res = max( res , cnt );
    }
    cout << res << "\n";
    return 0;
}

失衡天平

\(f[i][j]\)从前 i 个物品中选择,且重量差为\(j\)时可以选择到的最大重量。转移的时候其实考虑三种情况

  1. \(a_i\)不要
  2. \(a_i\)要,且放在之前更重的一侧,此时\(j\)会变大。
  3. \(a_i\)要,且放在之前更轻的一侧,此时\(j\)会变小,但是\(j\)可能会变成负数,所以要取绝对值
#include <bits/stdc++.h>

using namespace std;

#define int long long

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, k, m = 0;
    cin >> n >> k;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
        cin >> a[i], m += a[i];
    vector f(n + 1, vector<int>(m + 1, INT_MIN));
    f[0][0] = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= m; j++){
            f[i][j] = f[i-1][j];
            f[i][j] = max( f[i][j] , f[i-1][ abs( j - a[i] ) ] + a[i] );
            if( j + a[i] <= m ) f[i][j] = max( f[i][j] , f[i-1][j+a[i]] + a[i] );
        }

    }
    int res = 0;
    for( int i = 0 ; i <= k ; i ++ )
        res = max( res , f[n][i] );
    cout << res << "\n";
    return 0;
}

多重背包

#include <bits/stdc++.h>

using namespace std;

#define int long long


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n , m;
    cin >> n >> m;
    vector<int> t(n+1) , w(n+1) , v(n+1);
    for( int i = 1 ; i <= n ; i ++ )
        cin >> t[i] >> w[i] >> v[i];
    vector f( n+1 , vector<int>(m) );
    int res = 0;
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 0 ; j <= m ; j ++ ){
            f[i][j] = f[i-1][j];
            for( int k = 1 ; k <= t[i] && k * w[i] <= j ; k ++ )
                f[i][j] = max( f[i][j] , f[i-1][ j - k * w[i] ] + k * v[i] );
            res = max( res , f[i][j] );
        }
    cout << res << "\n";
    return 0;
}

货币系统

从小到大逐个检查当前货币能否被替代。

#include <bits/stdc++.h>

using namespace std;

#define int long long

void solve() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto &i: a)
        cin >> i;
    sort(a.begin(), a.end());
    vector<int> v(a.back() + 1);
    int res = 0;
    for (auto i: a) {
        if (v[i]) continue;
        v[i] = 1, res++;
        for (int j = 0; i + j < v.size(); j++)
            if (v[j] ) v[i + j] = 1;
    }

    cout << res << "\n";
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int t;
    cin >> t;
    for (; t; t--)
        solve();
    return 0;
}

合并回文子串

\(f[lA][rA][lB][rB]\)表示字符串\(A[lA,rA]\)\(B[lB,rB]\)能否拼成回文串,然后考虑区间 dp 去扩展。因为要求了串内是顺序不能改变,所以新扩展的只能是\((A[lA],A[rA]),(B[lB],B[rB]),(A[lA],B[rB]),(B[lB],A[rA])\)四种情况。逐个去判断能够扩展即可。注意所有长度小于等于 1 的串都可以认为是回文串。

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 55;
int f[N][N][N][N];


void solve() {
    string a, b;
    cin >> a >> b;
    int n = a.size(), m = b.size();
    memset( f , 0 , sizeof(f) );
    int res = 0;
    for (int lenA = 0; lenA <= n; lenA++)
        for (int lenB = 0; lenB <= m; lenB++)
            for (int lA = 1, rA = lenA; rA <= n; lA++, rA++)
                for (int lB = 1, rB = lenB; rB <= m; lB++, rB++) {
                    if (lenA + lenB <= 1) f[lA][rA][lB][rB] = 1;
                    else {
                        if (rA > 0 && a[lA - 1] == a[rA - 1])
                            f[lA][rA][lB][rB] |= f[lA + 1][rA - 1][lB][rB];
                        if (rB > 0 && b[lB - 1] == b[rB - 1])
                            f[lA][rA][lB][rB] |= f[lA][rA][lB + 1][rB - 1];
                        if (rB > 0 && a[lA - 1] == b[rB - 1])
                            f[lA][rA][lB][rB] |= f[lA + 1][rA][lB][rB - 1];
                        if (rA > 0 && b[lB - 1] == a[rA - 1])
                            f[lA][rA][lB][rB] |= f[lA][rA - 1][lB + 1][rB];
                    }
                    if (f[lA][rA][lB][rB]) res = max(res, lenA + lenB);
                }
    cout << res << "\n";
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int t;
    cin >> t;
    for (; t; t--)
        solve();
    return 0;
}

没有上司的舞会

\(f[i][0/1]\)表示\(i\)的子树中\(i\)不选或选的最大收益

#include <bits/stdc++.h>

using namespace std;

#define int long long

vector<int> a;
vector<array<int,2>> f;
vector<vector<int>> e;

void dfs( int i){
    f[i][1] = a[i];
    for( int j : e[i] ){
        dfs(j);
        f[i][1] += f[j][0];
        f[i][0] += max(f[j][0] , f[j][1]);
    }
    return ;
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    a=vector<int>(n+1);
    f = vector<array<int,2>>(n+1);
    e = vector<vector<int>>(n+1);
    for( int i = 1 ; i <= n ; i ++ )
        cin >> a[i];
    int root = n * (n+1) / 2;
    for( int i = 1 , x , y ; i < n ; i ++ )
        cin >> x >> y , e[y].push_back(x) , root -= x;
    dfs(root);
    cout << max( f[root][0] , f[root][1] );
    return 0;
}

小G有一个大树

统计子树大小\(cnt_i\),这\(f_i=\max( n - cnt_i,cnt_j)\),其中\(j\)\(i\)的子节点

#include <bits/stdc++.h>

using namespace std;

#define int long long

int n;
vector<vector<int>> e;
vector<int> cnt, fa;

void dfs(int x) {
    cnt[x] = 1;
    for (auto y: e[x]) {
        if (y == fa[x]) continue;
        fa[y] = x, dfs(y);
        cnt[x] += cnt[y];
    }
    return;
}

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
    while (cin >> n) {
        e = vector<vector<int>>(n + 1), cnt = vector<int>(n + 1, -1), fa = vector<int>(n + 1, -1);
        for (int x, y, i = 1; i < n; i++)
            cin >> x >> y, e[x].push_back(y), e[y].push_back(x);
        dfs(1);
        int res = -1, val = INT_MAX;
        for (int i = 1, f; i <= n; i++) {
            f = n - cnt[i];
            for (auto y: e[i])
                if( y != fa[i] ) f = max(f, cnt[y]);
            if (val > f) val = f, res = i;
        }
        cout << res << " " << val << "\n";
    }
    return 0;
}

石子合并

首先破环成链,然后然后这道题就可以转换成区间 dp。

先考虑最大得分

\(f[l][r]\)表示把\([l,r]\)合并的最大得分,枚举合并的中间点\(k\),则\(f[l][r] = \max( f[l][k] + f[k+1][r] +\sum a[i])\)

这里注意,当前区间一定是从更短的区间转移过来,所以枚举区间的时候,先枚举区间长度,然后根据左端点算出右端点,同样是\(O(N^2)\)的枚举,这样可以保证更短的区间一定会更早的被枚举。最小得分操作基本相同。

#include <bits/stdc++.h>

using namespace std;

const int N = 405;
int a[N], f[N][N], g[N][N];

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    memset(g, 0x3f, sizeof g);
    for (int i = 1; i <= n; i++) cin >> a[i], a[i + n] = a[i];
    for (int i = 1; i <= n * 2; i++) a[i] += a[i - 1], g[i][i] = 0;
    for (int len = 2; len <= n; len++) {
        for (int l = 1, r = len; r <= 2*n; l++, r++) {
            for (int k = l, W = a[r] - a[l - 1]; k < r; k++) {
                f[l][r] = max(f[l][r], f[l][k] + f[k + 1][r] + W);
                g[l][r] = min(g[l][r], g[l][k] + g[k + 1][r] + W);
            }
        }
    }
    int res = INT_MIN, ans = INT_MAX;
    for (int l = 1, r = n; l <= n; l++, r++)
        res = max(res, f[l][r]), ans = min(ans, g[l][r]);
    cout << ans << "\n" << res;
    return 0;
}

炮兵阵地

首先统计出所有可能的合法放置方法。

设状态\(f[i][j][k]\)表示前\(i\)行且第\(i,i-1\)行状态是\(j,k\)情况下最多可以放的炮兵数量。

对于第\(i\)我们可以暴力的枚举出第\(i,i-1,i-2\)行的状态\(j,k,l\),判定合法后就可以转移\(f[i][j][k]=\sum f[i-1][k][l] + val[j]\)

#include <bits/stdc++.h>

using namespace std;

#define int long long

#define lowbit(x) ( x & -x )

typedef bitset<4> T;

int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    int M = (1 << m) - 1;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
        string s;
        cin >> s;
        for (auto j: s)
            a[i] = (a[i] << 1) | (j == 'H');
    }
    vector<array<int, 2>> P;
    for (int i = 0, j, t; i <= M; i++) {
        if (i & ((i << 1) | (i << 2) | (i >> 1) | (i >> 2))) continue;
        j = i, t = 0;
        while (j) t++, j -= lowbit(j);
        P.push_back({i, t});
    }

    int N = P.size();
    vector f(N, vector(N, 0ll)), g(N, vector(N, 0ll));
    for (int i = 0; i < N; i++) {
        if (a[1] & P[i][0]) continue;
        for (int j = 0; j < N; j++) {
            if (P[j][0] & (P[i][0] | a[0])) continue;
            g[i][j] = P[j][1] + P[i][1];
        }
    }

    for (int i = 2; i < n; i++) {

        for (int j = 0; j < N; j++) {
            if (P[j][0] & a[i]) continue;
            for (int k = 0; k < N; k++) {
                if (P[k][0] & (a[i - 1] | P[j][0])) continue;
                for (int l = 0; l < N; l++) {
                    if (P[l][0] & (a[i - 2] | P[k][0] | P[j][0])) continue;
                    f[j][k] = max(f[j][k], g[k][l] + P[j][1]);
                }
            }
        }
        g.swap(f), f = vector(N, vector(N, 0ll));
    }
    int res = 0;
    for (const auto &i: g)
        res = max(res, *max_element(i.begin(), i.end()));
    cout << res << "\n";
    return 0;
}

传纸条

一正一反找两条路径,等价于正着直接找两条不相同的路径。因为\((n,m)\)的权值为零,所以我们可以直接找两条路分别到达\((n,m-1),(n-1,m)\)。如何实现过程中不想交?保证在每一行第一条路径到达\((i,j)\)后,第二条路径只能到达\((i,j+1)\)及其右侧的点即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 55;
int g[N][N] , f[N][N][N][N];


int32_t main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    for( int i = 1 ; i <= n  ; i ++ )
        for( int j = 1 ; j <= m ; j ++)
            cin >> g[i][j];
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 1 ; j <= m ; j ++ )
            for( int x = 1 ; x <= n ; x ++ )
                for(  int y = j+1 ;y <= m ; y ++ ){
                    f[i][j][x][y] = max( {f[i][j-1][x][y-1] , f[i][j-1][x-1][y] , f[i-1][j][x][y-1],f[i-1][j][x-1][y]}) + g[i][j] + g[x][y];
                }
    cout << f[n][m-1][n-1][m];
    return 0;
}
posted @ 2023-07-28 09:36  PHarr  阅读(3)  评论(0编辑  收藏  举报