codeforces-946D 预处理+分组背包

题目链接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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值