abc345e 做题小计
F 比 E 简单 ,套路题。
考场不会 E 。自闭。
题意已经讲得很清楚了。
在题解中,认为 \(m\) 等价于原题的 \(k\) 。
思考
第一步看题应该会想到贪心。
先去掉重复,然后会剩下一些相邻互不相同的,然后从小到大排序删除即可。
没错,考场上就是这样想的,直接吃了依托大的罚时
这样是不对的。
其实这样处理的问题是原问题的弱化版。
因为删除了 $l\to r $ 这一段后有可能 \(c_{l-1}=c_{r+1}\) 这不就错了吗。
那我做个集贸啊
观察到 \(k\le 500\) 肯定是出题人有意而为。
一段段取的问题 考虑 dp 。
Solution 1
通用套路。
我们定义状态 \(f_{i,j,0/1}\) 表示前 \(i\) 个数中是否删除 \(i\),并且已经删除了 \(j\) 个的最大价值。
那么显然有转移:
\(f_{i,j,0} = \max(f_{i-1,j,0},f_{i-1,j,1})+ w_i\)
\(f_{i,j,1} = \max([c_{i+1}\ne c_{k}]f_{k,j-(i-k),0})(\forall k\ge 0,j-(i-k)\ge 0)\)
时间复杂度 \(O(nm^2)\)
不是这式子一二项都有 \(k\) 我优化个集贸啊,还有个转移条件。
直接喜提 MLE TLE 大礼包。
Solution 2
从这开始就没想到了,上面是考场思路,这里是看了题解才知道的。
一二项有 \(k\) 不好优化?还有颜色判断?直接把颜色放成一维不就行了!
我们定义状态 \(f_{i,j,k}\) 表示前 \(i\) 个数 删除 \(j\) 个 最后颜色为 \(k\) 的方案。
你别看它好像是 \(O(n^3m)\) 的,实际是 \(O(n^2m)\) 的。
因为唯一一个转移的状态就是 \(f_{i,j,c_i}\) 其它直接从 \(i-1\) 的复制过来就行了。
这个转移很好写,看代码就能懂。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
int n,k,m;
int c[N],pos;
ll v[N];
ll maxx;
ll col[N],top,w[N];
ll f[501][501][501],ans;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%lld",&c[i],&w[i]);
memset(f,-127/3,sizeof f);
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{
f[i][0][0]=0;
for(int j=0;j<=m;j++)
{
if(j) for(int k=0;k<=n;k++) f[i][j][k]=f[i-1][j-1][k];
for(int k=0;k<=n;k++)
if(k^c[i]) f[i][j][c[i]]=max(f[i][j][c[i]],f[i-1][j][k]+w[i]);
}
}
return 0;
}
Solution 3
新的 dp !
虽然翻新了,但是很难搞啊。
思考我们最终求的是什么,不就是 \(\max(f_{n,m,1\to n})\) 吗。
每次转移的时候不就是求 \(\max (f_{i-1,j,k\ne c_i})\) 吗
根据每次转移只会更新一个状态,其实发现有很多无用状态。
在转移时,明显,我们只需要记两个值:不同颜色的最大值和次大值。
这样思路似乎就明了了!
我们定义新状态 \(f_{i,j,0/1}\) 表示 \(f_{i,j,k}(\forall 0\le k\le n)\) 的最大值 / 次大值。保证次大值和最大值的颜色不同。其中记 \(g_{i,j}\) 表示最大值的颜色是什么。
分类讨论一下当前颜色和取的上一个,我们改一下上面的代码,就能实现 \(O(1)\) 转移了!
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
int n,k,m;
int c[N],pos;
ll w[N];
ll f[N-4][501][2],ans;
int g[N-4][501];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%lld",&c[i],&w[i]);
memset(f,-127,sizeof f);
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j)
{
f[i][j][0]=f[i-1][j-1][0],f[i][j][1]=f[i-1][j-1][1];
g[i][j]=g[i-1][j-1];
}
ll v;
if(g[i-1][j]^c[i]) v=f[i-1][j][0]+w[i];
else v=f[i-1][j][1]+w[i];
if(v>f[i][j][0])
{
if(g[i][j]^c[i]) f[i][j][1]=f[i][j][0];
f[i][j][0]=v,g[i][j]=c[i];
}
else if(g[i][j]^c[i]) f[i][j][1]=max(f[i][j][1],v);
}
}
if(f[n][m][0]<0) f[n][m][0]=-1;
printf("%lld",f[n][m][0]);
return 0;
}
Solution 4
然而这份代码被卡空间了。
改成滚动数组即可。
时间复杂度 \(O(nk)\)
#include<bits/stdc++.h>
#define ll long long
#define N 200005
using namespace std;
int n,k,m;
int c[N],pos;
ll w[N];
ll f[2][501][2],ans;
int g[2][501];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%lld",&c[i],&w[i]);
memset(f,-127,sizeof f);
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{
int p=i&1,q=!p;
for(int j=0;j<=m;j++)
{
f[p][j][0]=f[p][j][1]=-1e18;g[p][j]=0;
if(j)
{
f[p][j][0]=f[q][j-1][0],f[p][j][1]=f[q][j-1][1];
g[p][j]=g[q][j-1];
}
ll v;
if(g[q][j]^c[i]) v=f[q][j][0]+w[i];
else v=f[q][j][1]+w[i];
if(v>f[p][j][0])
{
if(g[p][j]^c[i]) f[p][j][1]=f[p][j][0];
f[p][j][0]=v,g[p][j]=c[i];
}
else if(g[p][j]^c[i]) f[p][j][1]=max(f[p][j][1],v);
}
}
if(f[n&1][m][0]<0) f[n&1][m][0]=-1;
printf("%lld",f[n&1][m][0]);
return 0;
}