递推与递归二分

P1233 木棍加工

https://www.luogu.org/problem/P1233

 

问题可以理解成一个矩形的覆盖问题,木棍的长与宽就是矩形的长与宽,如果一个矩形能够完全覆盖另一矩形(即一个木棍的长和宽都大于等于另一木棍)则处理下一木棍就不需要准备时间,这样就实现了本题的目标,即让加工木棍的准备时间最短。

那么,怎样同时保证木棍的两个属性都具有单调性呢? 显然要先对一个属性排序sort大法好

接下来另一组属性的单调递减只需要求出不下降子序列的个数即可,因为从一个子序列转换到另一子序列只需一分钟的准备时间,所以不下降子序列的个数就是总共的准备时间。

从dilworth定理中,我们可知,下降子序列的最小划分等于最长不下降子序列的长度

于是题目转换为一道基本的dp求最长不下降子序列的题目

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct node
{
    int L;
    int W;
}a[5005];
int b[5005];
bool cmp(node x,node y)
{
    return x.L>y.L;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i].L,&a[i].W);
    }
    sort(a+1,a+1+n,cmp);

    int ans=0;
    //下降子序列的最小划分等于最长不下降子序列的长度
    for(int i=1;i<=n;i++)
    {
        if(a[i].W>b[ans])
        {
            b[ans+1]=a[i].W;
            ans++;
        }
        else
        {
            int l=1,r=ans;//这里相当于在当前b数组里面寻找a[i].w适合存的位子
            while(l<=r)
            {
                int mid=(l+r)/2;
                if(b[mid]>=a[i].W)
                  r=mid-1;
                else
                    l=mid+1;
            }
            b[l]=a[i].W;
        }
    }
    cout<<ans<<endl;
    return 0;
}

P1316 丢瓶盖

https://www.luogu.org/problemnew/show/P1316

 

方法就是二分答案。

注意在输入完坐标时,排序。(刚开始还疑惑要是没连着怎么办,我怎么这么蠢)

 代码注释可以很好理解这道题的解法

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int A,B;
ll ans;
ll a[100005];
bool check(ll dd)
{
    ll tot=1;
    ll num=1;
    for(int i=2;i<=A;i++)
    {
        if(a[i]-a[num]>=dd)
        {
            tot++;//我们当前想的是最大距离为dd,当大于等于了,就应该选一个点了
            num=i;//代表上一个被选择的点,作记录
        }
    }
    if(tot<B)//如果选的数还不足B,说明距离偏大,不符合要求
    {
        return false;
    }
     else//否则正确
        return true;
}
int main()
{
    scanf("%d%d",&A,&B);
    for(int i=1; i<=A; i++)
    {
        scanf("%lld",&a[i]);
    }
    sort(a+1,a+1+A);
    ll l=0,r=a[A]-a[1],mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid))
        {
             ans=mid;
             l=mid+1;
        }
        else
            r=mid-1;
    }
    cout<<ans<<endl;
    return 0;
}

P1182 数列分段 Section II

https://www.luogu.org/problemnew/show/P1182

//注意l不能从0开始,数字是不可以分开的,可能没有为0的数字,
//所以要从最大的数开始,保证数字没有拆分 

 

//注意l不能从0开始,数字是不可以分开的,可能没有为0的数字,
//所以要从最大的数开始,保证数字没有拆分
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100100],n,m;
bool check(int x)
{
    int sum = 0, cnt = 1;
    for (int i=1; i<=n; ++i)
    {
        sum += a[i];
        if (sum>x)
        {
            cnt++;
            sum = a[i];
            if (cnt>m) return false ;
        }
    }
    return true ;
}
int main()
{
    int l = 0, r = 0, ans;
    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; ++i)
        scanf("%d",&a[i]), r += a[i], l = max(l,a[i]);
    while (l<=r)    //l不能从0开始,
    {
        int mid = (l+r)>>1;
        if (check(mid))//符合要求
        {
            ans = mid;//即可以更新值
            r = mid-1;//但我们需要的是分段和最大值的最小值,所以让r减小
        }
        else l = mid+1;
    }
    printf("%d",ans);
    return 0;
}

P1057 传球游戏

https://www.luogu.org/problemnew/show/P1057

做题的时候不要去想怎么数怎么数,不要想多么复杂,这样会做不出来题的。

直接从题意思考,

 设a[i][k]代表经过k次传到了i的方案数

 a[1][0]=1;//没有传 到本身的方案数当然是1咯
    //我们需要的答案是a[1][m]
    //不要把问题想复杂,这就是dp呀
    //传到i号同学的球只能来自于i的左边和右边
    //所以a[i][k]=a[i-1][k-1]+a[i+1][k-1]
    //然后对于1,n的情况要单独处理,a[1][k]=a[2][k-1]+a[n][k-1]
    //a[n][k]=a[n-1][k-1]+a[1][k-1]

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int n,m;
int  a[35][35];
//a[i][k]代表经过k次传到了i的方案数
int main()
{
    scanf("%d%d",&n,&m);
    a[1][0]=1;//没有传 到本身的方案数当然是1咯
    //我们需要的答案是a[1][m]
    //不要把问题想复杂,这就是dp呀
    //传到i号同学的球只能来自于i的左边和右边
    //所以a[i][k]=a[i-1][k-1]+a[i+1][k-1]
    //然后对于1,n的情况要单独处理,a[1][k]=a[2][k-1]+a[n][k-1]
    //a[n][k]=a[n-1][k-1]+a[1][k-1]
    for(int k=1;k<=m;k++)
    {

        for(int j=2;j<n;j++)
        {
            a[j][k]=a[j-1][k-1]+a[j+1][k-1];
        }
        a[1][k]=a[2][k-1]+a[n][k-1];
        a[n][k]=a[n-1][k-1]+a[1][k-1];

    }
    cout<<a[1][m]<<endl;
    return 0;
}

P1192 台阶问题

https://www.luogu.org/problemnew/show/P1192

找规律太重要了,思考能力也比较重要啊。

 可以自己模拟一下,可以发现每一个点的递推关系为s[n]=求和(s[n-k]~s[n-1])
就可以想象为最后一步:越1~k个台阶的情况,相加起来

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int mod=100003;
int s[100005];
int n,k;//最高n层,最多一次k步,求方案数
//可以自己模拟一下,可以发现每一个点的递推关系为s[n]=求和(s[n-k]~s[n-1])
//就可以想象为最后一步:越1~k个台阶的情况,相加起来
int main()
{
    scanf("%d%d",&n,&k);
    s[0]=1;//初始化,一种情况
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=k&&(i-j)>=0;j++)
        {
            s[i]=(s[i]+s[i-j])%mod;
        }
    }
    cout<<s[n]<<endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值