[APIO/CTSC 2007]数据备份【WQS二分】

本文介绍了一种结合WQS二分法和动态规划(DP)的策略,用于解决特定类型的树形结构问题。通过实例分析,展示了如何通过删除边上的数值并优化选择过程,达到求解最小化总和的目标。文章提供了详细的代码实现和样例验证,为理解复杂问题提供了一种新的视角。

BZOJ 1150

P 3620


  看到网上有很多的题解是用链表加上贪心来做的,但是可以去联想“tree【WQS二分+MST】”这道题,可以发现,如果我们对所有的边去删除一个数,我们可以把这道题做成线形的。

  我们可以去对所有的边(一共N-1条边)都删去一个数,然后我们可以去想办法去取其中的尽可能多的边,要保证取的边是负数的,然后去看最后的取到的边的个数与K的关系,然后就是一个二分答案的过程了,但是这个二分有点不一样,它有可能取不到真实答案的会直接“K-1\rightarrow K+1”这样类似的

  这里就是用到了WQS二分的原因了。

  在这里WQS的方案是不唯一的,我在这里的假设就是我们尽可能的取到小的权值(因为减去的是mid(也就是二分答案的数)),然后如果权值相同,我们去取此时放进去的数量更多的。那么趋于答案的正解肯定在K+1这个方向,并且趋于K,那么我们最后放回的正解应该是

return dp[N - 1][0].cnt >= K;

  但是正如我说的那样,答案的正解不唯一,WQS的原则就是:前提是可行解,再者要最趋近于正解。

所以按照上述方法推出来的WQS和DP转移方程就是:

include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, K, s[maxN];
ll d[maxN];
struct node
{
    ll val;
    int cnt;
    node(ll a=0, int b=0):val(a), cnt(b) {}
    friend bool operator < (node e1, node e2) { return e1.val == e2.val ? e1.cnt > e2.cnt : e1.val < e2.val; }
}dp[maxN][2];
inline bool check(ll limit)
{
    for(int i=2; i<N; i++) dp[i][0] = dp[i][1] = node(INF, 0);
    dp[1][1] = node(d[1] - limit, 1);
    dp[1][0] = node();
    for(int i=2; i<N; i++)
    {
        dp[i][0] = min(dp[i-1][1], dp[i-1][0]);
        dp[i][1] = node(dp[i - 1][0].val + d[i] - limit, dp[i - 1][0].cnt + 1);
    }
    dp[N - 1][0] = min(dp[N - 1][0], dp[N - 1][1]);
    return dp[N - 1][0].cnt >= K;
}
int main()
{
//    freopen("testdata.in", "r", stdin);
    scanf("%d%d", &N, &K);
    for(int i=1; i<=N; i++) scanf("%d", &s[i]);
    for(int i=2; i<=N; i++) d[i - 1] = s[i] - s[i - 1];
    ll l = 0, r = s[N], mid = 0;
    ll ans = r;
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if(check(mid))
        {
            r = mid - 1;
            ans = dp[N - 1][0].val + K * mid;
        }
        else l = mid + 1;
    }
    printf("%lld\n", ans);
    return 0;
}

 

这里再给几组样例:
 

5 2
0 2 5 6 8
6 2
0 1 3 5 7 9
4 1
0 5 10 15

当然,这道题的方法是不唯一的:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e5 + 7;
int N, K, s[maxN];
ll d[maxN];
struct node
{
    ll val;
    int cnt;
    node(ll a=0, int b=0):val(a), cnt(b) {}
    friend bool operator < (node e1, node e2) { return e1.val == e2.val ? e1.cnt < e2.cnt : e1.val < e2.val; }
}dp[maxN][2];
inline bool check(ll limit)
{
    for(int i=2; i<N; i++) dp[i][0] = dp[i][1] = node(INF, 0);
    dp[1][1] = node(d[1] - limit, 1);
    dp[1][0] = node();
    for(int i=2; i<N; i++)
    {
        dp[i][0] = min(dp[i-1][1], dp[i-1][0]);
        dp[i][1] = node(dp[i - 1][0].val + d[i] - limit, dp[i - 1][0].cnt + 1);
    }
    dp[N - 1][0] = min(dp[N - 1][0], dp[N - 1][1]);
    return dp[N - 1][0].cnt <= K;
}
int main()
{
//    freopen("testdata.in", "r", stdin);
    scanf("%d%d", &N, &K);
    for(int i=1; i<=N; i++) scanf("%d", &s[i]);
    for(int i=2; i<=N; i++) d[i - 1] = s[i] - s[i - 1];
    ll l = 0, r = s[N], mid = 0;
    ll ans = r;
    while(l <= r)
    {
        mid = (l + r) >> 1;
        if(check(mid))
        {
            l = mid + 1;
            ans = dp[N - 1][0].val + K * mid;
        }
        else r = mid - 1;
    }
    printf("%lld\n", ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值