好题总结汇总
总结一些做完很有收获的题。
一、经典问题 + DP的结合
1、题意:
给定 n n n 种颜色的球的数量 a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1,a2,...,an,选出一些不同种类的球(也就是在n种球中选球的任意情况),将球两两组合并且这两个球不能一致,或者将1个球看成一组,找到选出来的这些球的最小组数,球所有情况的最小组合数之和。
2、总结:
(1) 先对 a a a 数组进行排序。
(2) 假如我们已经选到了
n
n
n 个球的数量
a
1
,
a
2
,
.
.
.
,
a
n
,
a
1
≤
a
2
≤
.
.
.
≤
a
n
a_1, a_2, ..., a_n, a_1 \le a_2 \le ... \le a_n
a1,a2,...,an,a1≤a2≤...≤an 按照k个不同小球组合在一起或者是1个个组合的最小组合数是多少?
结论是:
r
e
s
u
l
t
=
m
a
x
(
a
[
n
]
,
c
e
i
l
(
∑
a
[
i
]
/
k
)
)
result = max(a[n], ceil(\sum{a[i]} / k))
result=max(a[n],ceil(∑a[i]/k))
(3) 当我们有个这个结论,我们就可以直接进行背包
d
p
dp
dp 了,定义
d
p
dp
dp 数组为:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],表示前i个物品中必选
a
[
i
]
a[i]
a[i],
a
[
i
]
a[i]
a[i] 为最大值,且体积为
j
j
j 的方案数。
转移方程为:
d
p
[
0
]
[
0
]
=
1
,
d
p
[
i
]
[
j
]
=
∑
d
p
[
1....
i
−
1
]
[
j
]
dp[0][0] = 1, dp[i][j] = \sum{dp[1....i-1][j]}
dp[0][0]=1,dp[i][j]=∑dp[1....i−1][j],复杂度
O
(
n
2
)
O(n^2)
O(n2)。
(4) 求取答案, A n s = ∑ d p [ i ] [ j ] ∗ m a x ( a [ i ] , c e i l ( j / k ) ) Ans = \sum{dp[i][j] * max(a[i], ceil(j / k))} Ans=∑dp[i][j]∗max(a[i],ceil(j/k))
3、代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5010 + 10;
const int mod = 998244353;
int n, m;
int a[N];
int dp[N][N];
void solve() {
cin >> n;
for(int i = 1; i <= n; ++ i ) cin >> a[i];
sort(a + 1, a + 1 + n);
dp[0][0] = 1;
int su[N] {0};
su[0]=1;
dp[0][0]=1;
for(int i = 1; i <= n; ++ i ) {
for(int j = a[i]; j <= 5000; ++ j ) {
dp[i][j] += su[j-a[i]];
// su[j] = (dp[i][j] + su[j]) % mod;
}
for(int j = 0; j <= 5000; ++ j )
su[j] = (su[j] + dp[i][j]) % mod;
}
// cout<<dp[1][1]<<endl;
int ans = 0;
for(int i = 1; i <= n; ++ i )
for(int j = 0; j <= 5000; ++ j ) {
ans = (ans + dp[i][j] * max((int)ceil(j * 1.0 / 2), a[i]) % mod) % mod;
// cout<<dp[i][j]<<' '<<max((int)ceil(j * 1.0 / 2), a[i])<<endl;
}
cout<<ans<<endl;
}
signed main() {
int ts = 1;
// cin >> ts;
while(ts -- ) solve();
return 0;
}
二、数学公式推导
1、题意:
2、题解:
非常妙的一道题,直接开始推导:
假设我们划分
a
a
a 个’L’形,
b
b
b 个2*2连通块。
我们可以得到一个方程组:
设
T
T
T 是一个未知的非负整数,满足
a
∗
3
+
b
∗
4
=
T
a * 3 + b * 4 = T
a∗3+b∗4=T
b
=
(
T
−
3
∗
a
)
/
4
b=(T{-}3*a)/4
b=(T−3∗a)/4
f
(
a
,
b
)
=
f
(
a
,
(
T
−
3
∗
a
)
/
4
)
=
a
∗
x
+
b
∗
y
=
a
∗
x
+
(
T
−
3
∗
a
)
/
4
∗
y
=
(
x
−
3
∗
y
/
4
)
∗
a
+
T
∗
y
/
4
f(a, b) = f(a,(T{-}3*a)/4) = a * x + b * y = a * x + (T{-}3*a)/4*y = (x-3*y/4)*a+T*y/4
f(a,b)=f(a,(T−3∗a)/4)=a∗x+b∗y=a∗x+(T−3∗a)/4∗y=(x−3∗y/4)∗a+T∗y/4
我们发现就是一个有关a的一次函数,我们分类讨论根据单调性求即可。
3、代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int n,x,y;
signed main() {
cin>>n>>x>>y;
if(n==2){
cout<<max(x,y)<<endl;
return 0;
}
if(x * 4 == 3 * y) {
cout<<y*n*n/4<<endl;
return 0;
}
if(x * 4 <= 3 * y) {
cout<<n*n/4*y<<endl;
}
else {
int ans=n*n/3*x;
if(n*n%3) {
ans-=(x-max(x,y));
}
cout<<ans<<endl;
}
return 0;
}