2021 ICPC 银川 B (2022 ICPC 沈阳热身赛) The Great Wall

题意:

给你一个长度为n的序列。对于每一个k,k∈[1,n].问你将其分成k个段,每个段的贡献为该段最大值-最小值。贡献总和最大值是多少.n≤1e3

分析:

很好写出一个朴素的dp

dp[i][k]=dp[j][k-1]+MAX a(j+1,i)-MIN a(j+1,i) 其中0<j<i

但是复杂度不允许 此时想如果能优化就好了 但是最大最小压根没法优化 没有传递性 所以此时再想优化一定会暴毙的

但是dp[i][k]一定是跑不掉的 这两维一定是固定的

考虑每个数是否造成贡献 一个数可能对贡献+ 对贡献— 不选他贡献0 选两次他贡献0 这四种情况

所以重新设计状态

dp[i][j][0/1/2] 表示 前i个数分成了j段 最后一段 0还没选一个数 1选了一个加数 2选了一个减数

最大化所选的数之和 最优解一定是每段的最大-最小

非常巧妙 用另一种描述的最优解 同样是题意的正解

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
#define fi first
#define se second
const int maxn = 1e3 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
int a[maxn] , dp[maxn][maxn][3];
int main()
{
    for (int i = 0 ; i < maxn ; i++)
        for (int j = 0 ; j < maxn ; j++)
            for (int k = 0 ; k <= 2 ; k++)
                dp[i][j][k] = -inf;

    ios::sync_with_stdio(false);
    int n; cin >> n;
    for (int i = 1 ; i <= n ; i++) cin >> a[i];
    dp[1][0][0] = 0;
    for (int i = 1 ; i <= n ; i++){
        for (int j = 0 ; j < i ; j++){
            if (dp[i][j][0] != -inf){
                // 不填
                dp[i + 1][j][0] = max(dp[i + 1][j][0] , dp[i][j][0]);
                // 填一个-1
                dp[i + 1][j][2] = max(dp[i + 1][j][2] , dp[i][j][0] - a[i]);
                // 填一个1
                dp[i + 1][j][1] = max(dp[i + 1][j][1] , dp[i][j][0] + a[i]);
                // 同时填 +1 -1
                dp[i + 1][j + 1][0] = max(dp[i + 1][j + 1][0] , dp[i][j][0]);
            }
            if (dp[i][j][1] != -inf){
                // 不填
                dp[i + 1][j][1] = max(dp[i + 1][j][1] , dp[i][j][1]);
                // 填一个-1
                dp[i + 1][j + 1][0] = max(dp[i + 1][j + 1][0] , dp[i][j][1] - a[i]);
            }
            if (dp[i][j][2] != -inf){
                // 不填
                dp[i + 1][j][2] = max(dp[i + 1][j][2] , dp[i][j][2]);
                // 填一个+1
                dp[i + 1][j + 1][0] = max(dp[i + 1][j + 1][0] , dp[i][j][2] + a[i]);
            }
        }
    }
    for (int i = 1 ; i <= n ; i++){
        cout << dp[n + 1][i][0] << endl;
    }
    return 0;
}

同样的一个模型

https://codeforces.com/contest/1473/problem/E

分析:

先将问题转化为 对于一条路径 我们选择一条边将其权值附为0 再选择一条边 将其权值乘2 最终要最小化答案

在最短路的时候多设置两维 dis[i][0/1][0/1] 分别是否表示选择了一条边赋值为0 是否表示选择了一条边权值乘2

因为最小化答案 最终答案一定是路径上 最大边赋值为0 最小边权值乘2

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
const int maxn = 5e5 + 5;
const int maxm = 5e5 + 5;
const int mod = 1e9 + 7;
int u[maxm] , v[maxm] , nextt[maxm] , first[maxn] , sign;
ll w[maxm];
void addedge(int x , int y , int z){
    u[++sign]=x;
    v[sign]=y;
    w[sign] = z;
    nextt[sign]=first[x];
    first[x]=sign;
}
struct Node{
    ll id , dis;
    int s0 , s1;
    Node (int n = 0, ll d = 1e17 , int s0 = 0 , int s1 = 0)
    {
        id = n;dis = d;
        this->s0 = s0;
        this->s1 = s1;
    }
    bool operator < (const Node & a)const
    {
        return dis > a.dis;
    }
};
int book[maxn][2][2] , n , m;
ll dis[maxn][2][2];
void Dijstra(ll s){
    priority_queue<Node>q;
    while(q.size()) q.pop();
    for (int i = 1 ; i <= n ; i++){
        for (int j = 0 ; j <= 1 ; j++){
            for (int k = 0 ; k <= 1 ; k++){
                book[i][j][k] = 0;
                dis[i][j][k] = 1e16;
            }
        }
    }
    dis[s][0][0] = 0;
    q.push(Node(s , 0 , 0 , 0));
    while(q.size())
    {
        Node g = q.top() ; q.pop();
        int id = g.id;
        ll dist = g.dis;
        int s0 = g.s0 , s1 = g.s1;
        if (book[id][s0][s1]) continue;
        book[id][s0][s1] = 1;
        for (int i = first[id] ; i ; i = nextt[i]){
            if (dis[v[i]][s0][s1] > dist + w[i]){
                dis[v[i]][s0][s1] = dist + w[i];
                q.push(Node(v[i] , dis[v[i]][s0][s1] , s0 , s1));
            }
            if (!s0 && dis[v[i]][1][s1] > dist){
                dis[v[i]][1][s1] = dist;
                q.push(Node(v[i] , dis[v[i]][1][s1] , 1 , s1));
            }
            if (!s1 && dis[v[i]][s0][1] > dist + 2 * w[i]){
                dis[v[i]][s0][1] = dist + 2 * w[i];
                q.push(Node(v[i] , dis[v[i]][s0][1] , s0 , 1));
            }
            if (!s0 && !s1 && dis[v[i]][1][1] > dist + w[i]){
                dis[v[i]][1][1] = dist + w[i];
                q.push(Node(v[i] , dis[v[i]][1][1] , 1 , 1));
            }
        }
    }
    return ;
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 1 ; i <= m ; i++){
        int x , y , z; cin >> x >> y >> z;
        addedge(x , y , z);
        addedge(y , x , z);
    }
    Dijstra(1);
    for (int i = 2 ; i <= n ; i++){
        cout << dis[i][1][1] << " ";
    }
    cout << endl;
    return 0;
}

posted @ 2022-11-17 11:05  wzx_believer  阅读(80)  评论(0编辑  收藏  举报