BZOJ1863 [ZJOI2006]trouble 皇帝的烦恼 [思维题,二分答案,动态规划]

皇 帝 的 烦 恼 皇帝的烦恼

题目描述见链接 .


正 解 部 分 \color{red}{正解部分}

当是一条链时, 答案显然为 max ⁡ ( A [ i − 1 ] + A [ i ] ) \max(A[i-1]+A[i]) max(A[i1]+A[i]), 但这是一个环, 需要考虑 A 1 A_1 A1 A n A_n An 的冲突关系 .

首先答案具有 单调性, 考虑二分答案, 二分出答案设为 m i d mid mid, 然后是 c h e c k ( ) check() check() 部分 .
再设 A n A_n An A 1 A_1 A1 的 最小集合交 大小为 m i n _ i n t e [ n ] min\_inte[n] min_inte[n], 我们的目标是使得 m i n _ i n t e [ n ] min\_inte[n] min_inte[n] 尽量小,
现在与 A 1 A_1 A1 不相交的集合 S S S 大小为 m i d − A 1 mid-A_1 midA1,
再考虑到 A n − 1 A_{n-1} An1 A n A_n An 的影响, 可选且与 A 1 A_1 A1不相交 的 集合大小 为 ∣ S ∣ = m i d − ( A 1 ∪ A n − 1 ) |S| = mid - (A_1∪A_{n-1}) S=mid(A1An1) .

S S S 中的集合显然都应该加入到 A n A_n An 中, 然后若还不够, 就只能使用与 A 1 A_1 A1 冲突且不与 A n − 1 A_{n-1} An1 冲突的集合了, 这会增加 m i n _ i n t e [ n ] min\_inte[n] min_inte[n] 的值 .
所以 ∣ S ∣ |S| S 越大越好, 进而 A 1 ∪ A n − 1 = A 1 + A n − 1 − ( A 1 ∩ A n − 1 ) A_1 ∪ A_{n-1} = A_1 + A_{n-1} - (A_1 ∩ A_{n-1}) A1An1=A1+An1(A1An1) 越小越好, 再进而 A 1 ∩ A n − 1 A_1 ∩ A_{n-1} A1An1 越大越好,

为了达到这个目的, 设 m a x _ i n t e [ i ] max\_inte[i] max_inte[i] 表示 A i ∩ A 1 A_i ∩ A_1 AiA1 的最大值, 则通过类似上方的分析, 可以得到以下状态转移方程,

m i n _ i n t e [ i ] = max ⁡ ( 0 , A i − ( m i d − A i − 1 − A 1 + m a x _ i n t e i − 1 ) ) min\_inte[i] = \max(0, A_i - (mid - A_{i-1} - A_1 + max\_inte_{i-1})) min_inte[i]=max(0,Ai(midAi1A1+max_intei1))
m a x _ i n t e [ i ] = min ⁡ ( A i , A 1 , ( m i d − A i − 1 ) ∩ A 1 = A 1 − m i n _ i n t e i − 1 ) max\_inte[i] = \min(A_i, A_1, (mid-A_{i-1})∩A_1=A_1-min\_inte_{i-1}) max_inte[i]=min(Ai,A1,(midAi1)A1=A1min_intei1) .

二分界限为 [ 0 , 3 ∗ A m a x ] [0, 3*A_{max}] [0,3Amax], 时间复杂度 O ( N l o g A m a x ) O(NlogA_{max}) O(NlogAmax) .


实 现 部 分 \color{red}{实现部分}

2 2 2 个要注意的点

  1. 关于 A 1 A_1 A1 A 2 A_2 A2 m a x _ i n t e , m i n _ i n t e max\_inte, min\_inte max_inte,min_inte 要特殊处理 .
  2. 在检查 m i d mid mid 是否可行时, 需要先检查是否 m i d > max ⁡ ( A i + A i − 1 ) mid > \max(A_i+A_{i-1}) mid>max(Ai+Ai1) .
#include<bits/stdc++.h>
#define reg register

const int maxn = 1e5 + 10;

int N;
int Ans;
int Max_A;
int A[maxn];
int min_inte[maxn];
int max_inte[maxn];

bool chk(int mid){
        min_inte[1] = max_inte[1] = A[1];
        for(reg int i = 1; i < N; i ++) if(A[i] + A[i+1] > mid) return false;
        for(reg int i = 2; i <= N; i ++){
                min_inte[i] = std::max(0, A[i] - (mid-A[i-1]-A[1]+max_inte[i-1]));
                min_inte[i] = std::min(min_inte[i], A[i]);
                max_inte[i] = std::min(A[1], A[1] - min_inte[i-1]);
                max_inte[i] = std::min(max_inte[i], A[i]);
                if(i == 2) min_inte[i] = max_inte[i] = 0;
//                printf("%d: %d %d\n", i, min_inte[i], max_inte[i]);
        }
        return !min_inte[N];
}

int main(){
        scanf("%d", &N);
        for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]), Max_A = std::max(Max_A, A[i]);
        if(N == 1){ printf("%d\n", A[1]); return 0; }
        int l = 0, r = 3*Max_A;
        Ans = 0x3f3f3f3f;
        while(l <= r){
                int mid = l+r >> 1;
                if(chk(mid)) Ans = std::min(Ans, mid), r = mid - 1;
                else l = mid + 1;
        }
        printf("%d\n", Ans);
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值