[Gym-101875E]Loppinha, the boy who likes sopinha(贪心,记忆化搜索)

vjudge链接

题面大意是有一个长度为n的01串,每一段连续长度为x的1串的花费为a_1=1,d=1的等差数列和S_x。问把最少多少个1变成0,可以让总花费小于m。

第一眼看到题目很容易想到一个错误的想法:每次取最大的1段二分,这样每一刀肯定是性价比最高的。每次贪心的取性价比最高的分法。(这样肯定是最优的嘛

然后再想一下,有可能会有一些性价比没这么高的分法,但是恰好花费比m小,而性价比高的分法的花费可能会比m小很多。同时使前者的改变次数比后者少。例如:

5 3
11111
前者用三等分的做法处理后为:10101,ans=2;
而已知后者ans=3;
  • 单的贪心性价比分法不能得到最优解,那么我们可不可以尝试一下贪心一下改变次数呢?
  • 每次枚举就计算一下当前次数的最少花费是否小于m。
  • 简单计算一下时间复杂度,枚举次数(n)*搜索(n*n)=(n^3)
  • 用记忆化搜索剪枝一下就是O(能过)O (n^2)
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 550;
const int inf = 1e9;
int n,m;
char s[maxn];
int dp[maxn][maxn];
inline int val(int x){
    return x*(x+1)/2;
}
inline int cal(int p,int k){
    if(k<0)return inf;
    if(p==n)return 0;
    int &res=dp[p][k],i;
    if(~res)return res;
    if(s[p]=='0')return cal(p+1,k);
    res=inf;
    for(i=p;i<n&&s[i]=='1';++i){
        res=min(res,cal(i+1,k-1)+val(i-p));
        if(!res)return 0;
    }
    res=min(res,cal(i,k)+val(i-p));
    return res;
}

int main(){
    memset(dp,-1,sizeof dp);
	scanf("%d%d%s",&n,&m,s);
	for(int i=0;i<=n;++i){
        if(cal(0,i)<=m){
            printf("%d\n",i);
            break;
        }
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值