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]:前
i
i
i个同学至少分到几个机房。
我们规定
s
u
m
[
i
]
[
k
]
:
sum[i][k]:
sum[i][k]:前
i
i
i个元素中有多少个
k
k
k。其中
k
∈
[
1
,
2
]
k\in[1,2]
k∈[1,2]。那么有以下三种状态时可能面临存在分段与否的抉择:
①
a
b
s
(
(
s
u
m
[
i
]
[
1
]
−
s
u
m
[
i
−
1
]
[
1
]
)
−
(
s
u
m
[
j
−
1
]
[
2
]
−
s
u
m
[
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[i−1][1])−(sum[j−1][2]−sum[j−1][2]))<=m:[j,i]这一段元素
1
1
1和
2
2
2的数量差绝对值
<
=
m
<=m
<=m。
②
s
u
m
[
i
]
[
1
]
−
s
u
m
[
j
−
1
]
[
2
]
=
=
0
:
[
j
,
i
]
sum[i][1]-sum[j-1][2]==0:[j,i]
sum[i][1]−sum[j−1][2]==0:[j,i]这一段只含元素
1
1
1。
③
s
u
m
[
i
]
[
2
]
−
s
u
m
[
j
−
1
]
[
2
]
=
=
0
:
[
j
,
i
]
sum[i][2]-sum[j-1][2]==0:[j,i]
sum[i][2]−sum[j−1][2]==0:[j,i]这一段只含元素
2
2
2。
而面临是否分段时,状态转移方程为:
f
[
i
]
=
m
i
n
(
f
[
i
]
,
f
[
j
−
1
]
+
1
)
;
f[i]=min(f[i],f[j-1]+1);
f[i]=min(f[i],f[j−1]+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;
}
思路②:一个很妙的简化思路,将所有
2
2
2改写为
−
1
-1
−1,题目则转变为找每段的和
<
=
m
<=m
<=m的情况下最少分为多少段。和上面一样的思路,令
s
u
m
[
i
]
sum[i]
sum[i]表示前
i
i
i个元素的和,只有当以下两种情况时面临分段与否的抉择:
①
a
b
s
(
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
)
<
=
m
:
[
j
,
i
]
abs(sum[i]-sum[j-1])<=m:[j,i]
abs(sum[i]−sum[j−1])<=m:[j,i]这一段
1
1
1和
−
1
-1
−1的数量差
<
=
m
<=m
<=m。
②
a
b
s
(
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
)
=
=
i
−
j
+
1
:
[
j
,
i
]
abs(sum[i]-sum[j-1])==i-j+1:[j,i]
abs(sum[i]−sum[j−1])==i−j+1:[j,i]这一段只含
1
1
1 或 只含
−
1
-1
−1。
代码如下:
#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;
}