题目链接https://codeforces.com/problemset/problem/946/D
codeforces好题,大致题意是:每天有几节课,总共n天,在这n天最多逃课k次,然后就是要求最多能节省多少时间。每天的课程是一个01串,每天需要上课的时间是从第一个’1’的位置i,到最后一个’1’的位置j,总的时间是j-i+1,然后逃课就是可以删除一些’1’。
首先要预处理处每天的逃k节课带来的收益,一开始我之间左右贪心,看了样例发现不能贪心。我也没想到O(n)预处理的办法,就直接暴力n^2了。
后面就是直接n维01背包。
代码
///预处理+分组背包
///普通分组背包就像是n维01背包??
///预处理也挺难的 而且不能从两端贪心 不然是错的 具体看第3个样例
///然后不会线性的预处理 只好暴力n^2
#include<bits/stdc++.h>
using namespace std;
#define maxn 507
#define maxm 10005
#define ll long long int
#define INF 0x3f3f3f3f
int n,m,p;
char s[maxn];
int dp[maxn][maxn],w[maxn][maxn],up[maxn];
vector<int>v;
int main()
{
scanf("%d%d%d",&n,&m,&p);
int ans=0;
memset(dp,-1,sizeof(dp));
for(int i=1;i<=n;i++){
scanf("%s",s);
v.clear();
for(int j=0;j<m;j++){
if(s[j]=='1')v.push_back(j);
}
int l=0,r=v.size()-1,tot=0;
if(l>r)continue;
ans+=v[r]-v[l]+1;
up[i]=r-l+1;
for(int j=0;j<=r;j++){
for(int k=j;k<=r;k++){
int num=j+r-k;
//printf("num = %d\n",num);
int cost=v[j]-v[l]+v[r]-v[k];
w[i][num]=max(w[i][num],cost);
}
}
w[i][up[i]]=v[r]-v[l]+1;
/*int sum=0;
while(l<r){
if(v[l+1]-v[l]>v[r]-v[r-1]){
sum+=v[l+1]-v[l];
w[i][++tot]=sum;
l++;
}
else {
sum+=v[r]-v[r-1];
w[i][++tot]=sum;
r--;
}
}
w[i][++tot]=sum+1;
up[i]=tot;*/
}
/*for(int i=1;i<=n;i++){
for(int j=1;j<=up[i];j++)printf("%d ",w[i][j]);
printf("\n");
}*/
dp[0][0]=0;
for(int k=1;k<=n;k++){
dp[k][0]=0;
if(up[k]==0){
for(int j=0;j<=p;j++)dp[k][j]=dp[k-1][j];
continue;
}
for(int i=1;i<=min(up[k],p);i++){
for(int j=p;j>=i;j--){
if(dp[k-1][j]!=-1&&dp[k-1][j-i]==-1)dp[k][j]=max(dp[k][j],dp[k-1][j]);
else if(dp[k-1][j]==-1&&dp[k-1][j-i]!=-1)dp[k][j]=max(dp[k][j],dp[k-1][j-i]+w[k][i]);
else if(dp[k-1][j]!=-1&&dp[k-1][j-i]!=-1)dp[k][j]=max(dp[k][j],max(dp[k-1][j],dp[k-1][j-i]+w[k][i]));
}
}
}
int ma=0;
for(int i=0;i<=p;i++)ma=max(dp[n][i],ma);
/*for(int i=0;i<=n;i++){
for(int j=0;j<=p;j++)printf("%d ",dp[i][j]);
printf("\n");
}
printf("ans = %d ma = %d\n",ans,ma);*/
printf("%d\n",ans-ma);
return 0;
}