为了能到远方,脚下的每一步都不能少.|

luckydrawbox

园龄:4个月粉丝:1关注:2

CF946D Timetable

题意

给出一个 nnmm 列的 01 串,对于每一行所要花费的代价是行中第一个 11 和最后一个 11 之间的距离加一,现在你有魔法可以去除掉 kk11 ,问去掉不多于 kk11 的情况下,你所能获得的最小代价是多少。

分析

显然,每一行从首尾开始去掉 11 才有贡献(指减少的代价),设某一行中 11 的位置为 a1,a2,,ae+1a_1,a_2,…,a_{e+1},则从左往右删的贡献依次为 a2a1,a3a2,,ae+1ae,1a_2-a_1,a_3-a_2,…,a_{e+1}-a_{e},1,于是设 lil_i 表示该行从左到右删除到第 ii11 的贡献(不删完),则 li=ai+1ai+li1(1ie)l_i=a_{i+1}-a_i+l_{i-1}(1\le i\le e),同理设 rir_i 为该行从右到左删除到第 ii11 的贡献(不删完),则 ri=aei+2aei+1+ri+1(1ie)r_i=a_{e-i+2}-a_{e-i+1}+r_{i+1}(1\le i\le e)

再设 wiw_i 为该行删 ii 个数的最大贡献,则 wi={maxj=0ilj+rij1ieli1+1i=e+1w_i=\begin{cases}\max_{j=0}^il_j+r_{i-j}&1\le i\le e\\l_{i-1}+1&i=e+1\end{cases},这样这题就转化成一个分组背包了,设 dpi,jdp_{i,j} 表示前 ii 行删 jj11 的最大贡献,就有转移方程 dpi,j=maxo=0jdpi1,o+wjodp_{i,j}=\max_{o=0}^j dp_{i-1,o}+w_{j-o}。(Qjo>e+1j-o>e+1 时会不会多算?A:多出的部分值为 00,不影响答案。)

最后 maxi=1kdpn,i\max_{i=1}^k dp_{n,i} 为可删除的最大代价,答案就是本来所有的代价和减去 maxi=1kdpn,i\max_{i=1}^k dp_{n,i}

实现时,我用 vector<int>\text{vector<int>} 类型的 ee 存储 aiai1a_i-a_{i-1},并求出初始的代价和 sumsum;如果某行是有 11 的,打上一个标记 flag=1flag=1,这样就能确定是否有删除到最后的 11,若没有就不需要在 ww 末尾处的贡献增加一个 li+1l_i+1;因为状态转移方程中每一项仅仅与前一项有关,所以我又加了个滚动数组优化。

最终时间复杂度 O(nm2+nk2)O(nm^2+nk^2),空间复杂度 O(m)O(m)

代码

#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 中国大陆许可协议进行许可。

posted @   luckydrawbox  阅读(1)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起