IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 2021牛客暑期多校训练营2——G、League of Legends -> 正文阅读

[数据结构与算法]2021牛客暑期多校训练营2——G、League of Legends

看了官方题解视频,觉得有*点简略,和评论区的感觉一样(
在这里插入图片描述

这里重新写一篇连铜牌都能看懂的 题解。

题意

给定 n 个区间 [ai,bi),要求将它们分成 k 组,最大化每组交的长度之和,并且每组的交长度不能为0。1≤k≤n≤5000 , 1≤a<b<105 。

思路

对于包含其他区间的大区间,我们考虑两种情况对其进行分配
1.归属到一个被它包含的区间所在的组,不影响答案
2.独自一组,长度直接算入答案
很显然,要么为第一种情况,要么为第二种情况,因此可以单独处理。
剩下的就是互不从属的小区间,我们令 d p i , j dp_{i,j} dpi,j?代表前j个数分成i组的交长度和, ( a i , b i ) (a_i,b_i) (ai?,bi?)代表剩下的小区间。
而每次转移只考虑最后一个多出来的组从哪(下标为k)开始
也就是可以写为
d p [ i ] [ j ] = d p [ i ? 1 ] [ k ] + b k + 1 ? a j dp[i][j] = dp[i-1][k]+b_{k+1}-a_j dp[i][j]=dp[i?1][k]+bk+1??aj?
其图示如下
在这里插入图片描述
显然,按照一般的枚举方式,我们需要分别枚举i,j,k三个维度。
此时的复杂度将会高达 O ( n 3 ) O(n^3) O(n3)
但我们观察发现:
原转移方程可以改写为
d p [ i ] [ j ] = m a x ( d p [ i ? 1 ] [ k ] + b k + 1 ) ? a j , ? w h e n ? b k + 1 > a j dp[i][j] = max(dp[i-1][k]+b_{k+1})-a_j, \ when \ b_{k+1} >a_j dp[i][j]=max(dp[i?1][k]+bk+1?)?aj?,?when?bk+1?>aj?
显然 m a x ( d p [ i ? 1 ] [ k ] + b k + 1 ) max(dp[i-1][k]+b_{k+1}) max(dp[i?1][k]+bk+1?)是可以使用单调队列进行维护最大值的。
不会维护的以及不知道为什么能优化的,请看这个博客中的例子:
https://www.cnblogs.com/ljy-endl/p/11638389.html
最后,我们再综合一下之前预处理的那些大区间,就得到了最后的答案。
代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 5e3+5;
struct node{
    int l,r;
}a[N*2],small[N*2];/*small 存储小区间*/
int bL[N],q[N],dp[N][N];/*bL 大区间*/
int main()
{
    memset(dp,-1,sizeof dp);
    int n,k;
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>a[i].l>>a[i].r;
    sort(a+1,a+n+1,[=](node x,node y){
        return x.l!=y.l?x.l<y.l:x.r>y.r;
    });
    int maxn = 0x3f3f3f3f;
    int cnt=0,tot=0;
    for(int i=n;i>0;i--)//处理大区间和小区间
    {
        if(a[i].r>=maxn)bL[++cnt] = a[i].r-a[i].l;
        else maxn = a[i].r,small[++tot]=a[i];
    }
    reverse(small+1,small+tot+1);
    sort(bL+1,bL+cnt+1,greater<int>());
    dp[0][0]=0;
    for(int i=1;i<=k;i++)
    {
        int h=1,t=0;
        for(int j=1;j<=tot;j++)
        {
            if(dp[i-1][j-1]>-1){//当最后一次转移合法
                while (h<=t&&
                    dp[i-1][q[t]]+small[q[t]+1].r<=dp[i-1][j-1]+small[j].r)
                    	t--;//队尾维护max(dp[i-1][k]+b_{k+1})
                q[++t]=j-1;
            }
            while (h<=t&&small[q[h]+1].r<=small[j].l)h++;//队首维护b_{k+1} >a_j
            if(h<=t)dp[i][j]=dp[i-1][q[h]]+small[q[h]+1].r-small[j].l;//找出了最优的max 所需的K,进行状态转移
        }
    }
    int tmp=0,ans=0;
    for(int i=0;i<=min(k,n);i++)
    {
        tmp+=bL[i];
        if(dp[k-i][tot]>-1){//所有小区间分成k-i段合法,此时还可包含i个大区间
            ans = max(ans,dp[k-i][tot]+tmp);
        }
    }
    cout<<ans<<endl;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-07-24 11:44:57  更:2021-07-24 11:45:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 17:59:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码