G.League of Legends
Zechariah_2001题解
对于可以包含其他区间的大区间,要使得答案最优无非就是两种分组方式:单独一组或者与被包含的区间一组。单独一组那么贡献就是区间长度;如果说与被包含的区间一组,对答案贡献为0,因为根据题意,如果有多个区间,添加区间实际上是添加限制,不会是当前答案变优。
把上面的覆盖别的区间的区间出去后,剩下的区间按照左端点升序排序后不难发现右端点同样也会升序排列。
设计dp:
状态表示:
f
j
,
i
f_{j,i}
fj,i考虑前
i
i
i个区间,分成
j
j
j组的最大值
状态转移:
f
j
,
i
=
max
{
f
j
−
1
,
k
−
1
+
R
k
−
L
i
}
,
R
k
>
L
i
f_{j,i}=\max\{f_{j-1,k-1}+\text R_k-\text L_i\},\text R_k>\text L_i
fj,i=max{fj−1,k−1+Rk−Li},Rk>Li
R k > L i \text R_k>\text L_i Rk>Li这个限制保证每组至少玩1分钟游戏
如果没有上述现之,可以搞一个前缀的 max { f j − 1 , k − 1 + R k } \max\{f_{j-1,k-1}+\text R_k\} max{fj−1,k−1+Rk}优化转移,加上此限制后需要单调队列优化转移。
Code1 暴力
895ms 竟然也能过???
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pli=pair<ll,int>;
constexpr ll mod=1e9+7;
//=============================
int rd()
{
int res=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res;
}
const int N=5010;
int n,m;
struct node
{
int l,r;
bool operator<(const node&o)const
{
return l<o.l||l==o.l&&r>o.r;
}
}a[N],b[N];
int seg[N],cnt,tot;
int f[N][N];// f[j][i] 前i个分成j组
int main()
{
n=rd(),m=rd();
for(int i=1;i<=n;i++) b[i].l=rd(),b[i].r=rd();
sort(b+1,b+1+n);
int maxr=0x3f3f3f3f;
for(int i=n;i;i--)
{
if(b[i].r>=maxr) //能够覆盖别的线段
seg[++tot]=b[i].r-b[i].l;
else
maxr=b[i].r,a[++cnt]=b[i];
}
sort(a+1,a+1+cnt);
memset(f,-0x3f,sizeof f);
f[0][0]=0;
for(int j=1;j<=cnt;j++)
for(int i=j;i<=cnt;i++)
for(int k=1;k<=i;k++)
if(a[k].r>a[i].l)
f[j][i]=max(f[j][i],f[j-1][k-1]+a[k].r-a[i].l);
sort(seg+1,seg+1+tot,greater<int>());
int ans=0,sum=0;
for(int i=0;i<=min(m,tot);i++)
{
sum+=seg[i];
if(f[m-i][cnt]!=-1) ans=max(ans,f[m-i][cnt]+sum);
}
cout<<ans<<'\n';
return 0;
}
Code2 单调队列
83ms
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pli=pair<ll,int>;
constexpr ll mod=1e9+7;
//=============================
int rd()
{
int res=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
return res;
}
const int N=5010;
int n,m;
struct node
{
int l,r;
bool operator<(const node&o)const
{
return l<o.l||l==o.l&&r>o.r;
}
}a[N],b[N];
int seg[N],cnt,tot;
int f[N][N];// f[j][i] 前i个分成j组
int q[N];
int main()
{
n=rd(),m=rd();
for(int i=1;i<=n;i++) b[i].l=rd(),b[i].r=rd();
sort(b+1,b+1+n);
int maxr=0x3f3f3f3f;
for(int i=n;i;i--)
{
if(b[i].r>=maxr) //能够覆盖别的线段
seg[++tot]=b[i].r-b[i].l;
else
maxr=b[i].r,a[++cnt]=b[i];
}
sort(a+1,a+1+cnt);
memset(f,-0x3f,sizeof f);
f[0][0]=0;
// f[j-1][k-1] + a[k].r a[k].r>a[i].l && k<=i
for(int j=1;j<=cnt;j++)
{
int tt=-1,hh=0;
for(int i=1;i<j;i++)
{
while(hh<=tt)
{
int k=q[tt];
if(f[j-1][i-1]+a[i].r>=f[j-1][k-1]+a[k].r) --tt;
else break;
}
q[++tt]=i;
}
for(int i=j;i<=cnt;i++)
{
while(hh<=tt)
{
int k=q[hh];
if(a[k].r<=a[i].l) hh++;
else break;
}
while(hh<=tt)
{
int k=q[tt];
if(f[j-1][i-1]+a[i].r>=f[j-1][k-1]+a[k].r) --tt;
else break;
}
q[++tt]=i;
int k=q[hh];
f[j][i]=max(f[j][i],f[j-1][k-1]+a[k].r-a[i].l);
}
}
sort(seg+1,seg+1+tot,greater<int>());
int ans=0,sum=0;
for(int i=0;i<=min(m,tot);i++)
{
sum+=seg[i];
if(f[m-i][cnt]!=-1) ans=max(ans,f[m-i][cnt]+sum);
}
cout<<ans<<'\n';
return 0;
}