首页 > 其他 > 详细

题解 P6918 [ICPC2016 WF]Branch Assignment

时间:2021-02-21 23:55:36      阅读:28      评论:0      收藏:0      [点我收藏+]

题目分析:

首先我们观察一下,肯定得先正着跑一遍最短路再建反图跑一遍最短路,这样可以对于所有的 \(i\) 求出 \(dis(b + 1, i)\) 以及 \(dis(i, b + 1)\)

然后我们继续观察,题目里面有一个条件是: 同一个子集内的点两两之间互相发送信息。

于是我们可以知道

性质一

  • 每一个点的贡献实际上只跟自己所处在的子集的大小有关,跟哪一些元素在一起其实并不重要。假设点 \(i\) 所处的集合大小为 \(siz\) ,那么它对于答案的贡献就会是 \((dis(i, b + 1) + dis(b + 1, i)) * (siz - 1)\)

下面我们把 \((dis(i, b + 1) + dis(b + 1, i))\) 叫做 \(w_i\)

性质二:

  • 根据性质一,我们可以知道,对于 \(w_i\) 相近的一些数我们总是将它们分到一组更优。为什么呢?感性的用贪心来思考这个问题,因为如果它们的 \(w_i\) 比较小,我们总是希望它们能够与相对较多 的元素在同一个集合,毫无疑问的,相近的一些搭配在一起更好,如果 \(w_i\) 比较大也是类似的。

通过性质二的分析,我们将 \([1,b]\) 所有的点按照 \(w_i\) 排序。问题就转化为了将一个长度为 \(b\)单调不减序列 划分成 \(s\) 段,每一段的花费为这一段的数的和 * (这一段的长度 \(-\) \(1\))。

这样的话我们就轻松写出一个 \(n^3\)\(dp\):

\(dp[i][j]\) 表示将前 \(i\) 个数分成 \(j\) 段的最小费用。

那么 \(dp[i][j] = Min(dp[k][j - 1] + (sum[i] - sum[k]) * (i - k - 1)); (0 <= k < i)\)

这样的话是肯定过不了的。

考虑优化:

首先上面 \(dp[i][j]\) 可以通过滚动数组优化为一维,这样子空间复杂度就变成了 O\((n)\)

在做划分第 \(j\) 段的时候,打表知道 随着 \(i\) 往右边移动,对应的最优决策点是不断右移的。

证明:

首先明确一点,我们进行划分的序列是 单调不减 的。下面称这个序列为 \(A\) 序列,这个序列的第 \(i\) 项我们就称之为 \(A_i\)

我们假设现在在对 \(dp[i]\) 进行决策,\(dp[i - 1]\) 的最佳决策点是 \(k (0 <= k < i)\)

那么倘若 \(dp[i]\) 的最优决策点为 \(j\) 并且 \(j < k\)

那么意味着 :

\(dp[k] + (sum[i] - sum[k]) * (i - k - 1) > dp[j] + (sum[i] - sum[j]) * (i - j - 1)\)

但是又同时有:

\(dp[k] + (sum[i - 1] - sum[k]) * (i - k - 2) < dp[j] + (sum[i - 1] - sum[j]) * (i - j - 2)\)

上下两式作差则有(中间的化简过程就略了,读者可以自行化简,其中重要的一步就是要将 \(sum[i]\) 转化为 \(sum[i - 1] + A[i]\)):

\((i - k - 2)A_i + sum[i] - sum[k] > (i - j - 2)A_i + sum[i] - sum[j]\)

这个式子显然就矛盾了。所以可以知道,无论如何最佳决策点都是 单调不减 的。

于是时间复杂度: O(\(n^2\)) , 空间复杂度: O(\(n\)) 就水过去了。

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read() {
    int x = 0 , flag = 1;
    char ch = getchar();
    for( ; ch > ‘9‘ || ch < ‘0‘ ; ch = getchar()) if(ch == ‘-‘) flag = -1;
    for( ; ch >= ‘0‘ && ch <= ‘9‘ ; ch = getchar()) x = (x << 3) + (x << 1) + ch - ‘0‘;
    return x * flag;
}
const int MAXN = 5e4 + 50;
int n,r,b,s;
int disA[MAXN], disB[MAXN],start[MAXN],tot = 0,book[MAXN];
int sum[MAXN], dp[MAXN][2];
struct Edge {
    int next, to, w;
} edge[MAXN << 1];

struct Dijk {
    int id, dis;
    bool operator <(const Dijk& A) const {return A.dis < dis;}
} ;

struct Point {
    int val,id;
} P[MAXN];

priority_queue <Dijk> q;

void add(int from, int to, int w) {
    edge[++tot].next = start[from];
    edge[tot].to = to;
    edge[tot].w = w;
    start[from] = tot;
    return ;
}

void Dijkstra(int *dis,int op) {
    memset(book,0,sizeof(book));
    for(int i = 1 ; i <= n ; i ++) dis[i] = 1e9 + 7;
    dis[b + 1] = 0;
    q.push({b + 1, 0});
    while(!q.empty()) {
        int x = q.top().id, d = q.top().dis; q.pop();
        if(dis[x] != d) continue; 
        book[x] = 1;
        for(int i = start[x] ; i ; i = edge[i].next) {
            int to = edge[i].to, w = edge[i].w;
            if(i % 2 == op) continue;
            if(dis[x] + w < dis[to]) {
                dis[to] = dis[x] + w;
                if(!book[to]) q.push({to, dis[to]});
            }
        }
    }
    return ;
}

bool cmp(Point a, Point b) {return a.val < b.val;}
int Q[5005];

signed main() {
    n = read(), b = read(), s = read(), r = read();
    for(int i = 1 ; i <= r ; i ++) {
        int u = read(), v = read(), w = read();
        add(u, v, w); add(v, u, w);
    }
    Dijkstra(disA, 0); Dijkstra(disB, 1);
    for(int i = 1 ; i <= n ; i ++) {
        P[i].val = disA[i] + disB[i];
        P[i].id = i;
    }
    sort(P + 1, P + 1 + b , cmp);
    for(int i = 1 ; i <= b ; i ++) sum[i] = sum[i - 1] + P[i].val;
    int now = 1;
    for(int i = 1 ; i <= b ; i ++) dp[i][1] = sum[i] * (i - 1);
    for(int j = 2 ; j <= s ; j ++) {
        now ^= 1;
        memset(Q, 0, sizeof(Q));
        for(int i = 1 ; i <= b ; i ++) dp[i][now] = 1e18 + 7;
        for(int i = j ; i <= b ; i ++) {
            for(int k = i - 1 ; k >= Q[i - 1] ; k --) {
                if(dp[k][now ^ 1] + (sum[i] - sum[k]) * (i - k - 1) < dp[i][now]) Q[i] = k;
                dp[i][now] = min(dp[i][now], dp[k][now ^ 1] + (sum[i] - sum[k]) * (i - k - 1));
            }
        }
    }
    cout << dp[b][now];
    return 0;
}

题解 P6918 [ICPC2016 WF]Branch Assignment

原文:https://www.cnblogs.com/MYCui/p/14426873.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!