CF946D Timetable
题意
给出一个 行 列的 01 串,对于每一行所要花费的代价是行中第一个 和最后一个 之间的距离加一,现在你有魔法可以去除掉 个 ,问去掉不多于 个 的情况下,你所能获得的最小代价是多少。
分析
显然,每一行从首尾开始去掉 才有贡献(指减少的代价),设某一行中 的位置为 ,则从左往右删的贡献依次为 ,于是设 表示该行从左到右删除到第 个 的贡献(不删完),则 ,同理设 为该行从右到左删除到第 个 的贡献(不删完),则 。
再设 为该行删 个数的最大贡献,则 ,这样这题就转化成一个分组背包了,设 表示前 行删 个 的最大贡献,就有转移方程 。(Q: 时会不会多算?A:多出的部分值为 ,不影响答案。)
最后 为可删除的最大代价,答案就是本来所有的代价和减去 。
实现时,我用 类型的 存储 ,并求出初始的代价和 ;如果某行是有 的,打上一个标记 ,这样就能确定是否有删除到最后的 ,若没有就不需要在 末尾处的贡献增加一个 ;因为状态转移方程中每一项仅仅与前一项有关,所以我又加了个滚动数组优化。
最终时间复杂度 ,空间复杂度 。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
long long read(){
long long x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
void write(long long x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=510;
int n,m,k;
vector<int>e;
ll w[N],l[N],r[N],dp[2][N],sum,mx;
int main(){
n=read();m=read();k=read();
for(int i=1;i<=n;i++){
bool flag=0;
e.clear();
for(int j=1,lst=0,x;j<=m;j++){
scanf("%1d",&x);
if(x){
if(lst){
e.push_back(j-lst);
sum+=j-lst;
}
else{
sum++;
flag=1;
}
lst=j;
}
}
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
memset(w,0,sizeof(w));
for(int j=0;j<e.size();j++)
l[j+1]=l[j]+e[j];
for(int j=e.size()-1,o=0;j>=0;j--,o++)
r[o+1]=r[o]+e[j];
for(int j=1;j<=e.size();j++)
for(int o=0;o<=j;o++)
w[j]=max(w[j],l[o]+r[j-o]);
if(flag)
w[e.size()+1]=l[e.size()]+1;
for(int j=1;j<=k;j++)
for(int o=0;o<=j;o++)
dp[i&1][j]=max(dp[i&1][j],dp[(i&1)^1][o]+w[j-o]);
}
for(int i=0;i<=k;i++)
mx=max(mx,dp[n&1][i]);
write(sum-mx);
return 0;
}
本文作者:luckydrawbox
本文链接:https://www.cnblogs.com/luckydrawbox/p/18526572
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步