洛谷P1564 膜拜【DP】【黄】

本文探讨了一种学校分配机房问题,学生崇拜神牛甲或乙,要求满足同神牛或崇拜者数量差不超过m的条件。通过状态转移方程,揭示如何计算最少机房数,适合1至2500位学生的数据规模。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Date:2022.02.20
题目描述
神牛有很多…当然…每个同学都有自己衷心膜拜的神牛.
某学校有两位神牛,神牛甲和神牛乙。新入学的 n 位同学们早已耳闻他们的神话。
所以,已经衷心地膜拜其中一位了。现在,老师要给他们分机房。但是,要么保证整个机房都是同一位神牛的膜拜者,或者两个神牛的膜拜者人数差不超过 m。另外,现在 n 位同学排成一排,老师只会把连续一段的同学分进一个机房。老师想知道,至少需要多少个机房。
输入格式
输入文件第一行包含两个整数 n 和 m。
第 2 到第 (n + 1) 行,每行一个非 1 即 2 的整数,第 (i + 1) 行的整数表示第 i 个同学崇拜的对象,1 表示甲,2 表示乙。
输出格式
输出一个整数,表示最小需要机房的数量。
输入输出样例
输入 #1复制
5 1
2
2
1
2
2
输出 #1复制
2
说明/提示
数据规模与约定
对于 30% 的数据,保证 1≤n,m≤50。
对于 100% 的数据,保证 1≤n,m≤2500。

思路①:
f[i]f[i]f[i]:前iii个同学至少分到几个机房。
我们规定sum[i][k]:sum[i][k]:sum[i][k]:iii个元素中有多少个kkk。其中k∈[1,2]k\in[1,2]k[1,2]。那么有以下三种状态时可能面临存在分段与否的抉择:
abs((sum[i][1]−sum[i−1][1])−(sum[j−1][2]−sum[j−1][2]))<=m:[j,i]abs((sum[i][1]-sum[i-1][1])-(sum[j-1][2]-sum[j-1][2]))<=m:[j,i]abs((sum[i][1]sum[i1][1])(sum[j1][2]sum[j1][2]))<=m[j,i]这一段元素111222的数量差绝对值<=m<=m<=m
sum[i][1]−sum[j−1][2]==0:[j,i]sum[i][1]-sum[j-1][2]==0:[j,i]sum[i][1]sum[j1][2]==0[j,i]这一段只含元素111
sum[i][2]−sum[j−1][2]==0:[j,i]sum[i][2]-sum[j-1][2]==0:[j,i]sum[i][2]sum[j1][2]==0[j,i]这一段只含元素222
而面临是否分段时,状态转移方程为:f[i]=min(f[i],f[j−1]+1);f[i]=min(f[i],f[j-1]+1);f[i]=min(f[i],f[j1]+1);
代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
typedef long long LL;
LL n,m,k,f[N],sum[N][3];
int main()
{
    cin>>n>>m;memset(f,0x3f,sizeof f);f[0]=0;f[1]=1;//第一个元素必为一段
    for(int i=1;i<=n;i++)
    {
        LL x;cin>>x;
        sum[i][x]=sum[i-1][x]+1;
        sum[i][!(x-1)+1]=sum[i-1][!(x-1)+1];
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            if(abs((sum[i][1]-sum[j-1][1])-(sum[i][2]-sum[j-1][2]))<=m || (sum[i][2]-sum[j-1][2]==0) || (sum[i][1]-sum[j-1][1]==0))//这里第一个表达式很妙。
                f[i]=min(f[i],f[j-1]+1);
    cout<<f[n];
    return 0;
}

思路②:一个很妙的简化思路,将所有222改写为−1-11,题目则转变为找每段的和<=m<=m<=m的情况下最少分为多少段。和上面一样的思路,令sum[i]sum[i]sum[i]表示前iii个元素的和,只有当以下两种情况时面临分段与否的抉择:
abs(sum[i]−sum[j−1])<=m:[j,i]abs(sum[i]-sum[j-1])<=m:[j,i]abs(sum[i]sum[j1])<=m[j,i]这一段111−1-11的数量差<=m<=m<=m
abs(sum[i]−sum[j−1])==i−j+1:[j,i]abs(sum[i]-sum[j-1])==i-j+1:[j,i]abs(sum[i]sum[j1])==ij+1[j,i]这一段只含111 或 只含−1-11
代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N = 3010;
typedef long long LL;
LL n,m,k,f[N],sum[N];
int main()
{
    cin>>n>>m;memset(f,0x3f,sizeof f);f[0]=0;f[1]=1;//第一个元素必为一段
    for(int i=1;i<=n;i++)
    {
        LL x;cin>>x;
        if(x==1) sum[i]=sum[i-1]+1;
        else sum[i]=sum[i-1]-1;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            if(abs(sum[i]-sum[j-1])<=m || abs(sum[i]-sum[j-1])==i-j+1) 
                f[i]=min(f[i],f[j-1]+1);
    cout<<f[n];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值