好题总结汇总

好题总结汇总

总结一些做完很有收获的题。


一、经典问题 + DP的结合

1、题意:

给定 nnn 种颜色的球的数量 a1,a2,...,ana_1, a_2, ..., a_na1,a2,...,an,选出一些不同种类的球(也就是在n种球中选球的任意情况),将球两两组合并且这两个球不能一致,或者将1个球看成一组,找到选出来的这些球的最小组数,球所有情况的最小组合数之和。

2、总结:

(1) 先对 aaa 数组进行排序。

(2) 假如我们已经选到了 nnn 个球的数量 a1,a2,...,an,a1≤a2≤...≤ana_1, a_2, ..., a_n, a_1 \le a_2 \le ... \le a_na1,a2,...,an,a1a2...an 按照k个不同小球组合在一起或者是1个个组合的最小组合数是多少?
结论是:
result=max(a[n],ceil(∑a[i]/k))result = max(a[n], ceil(\sum{a[i]} / k))result=max(a[n],ceil(a[i]/k))

(3) 当我们有个这个结论,我们就可以直接进行背包 dpdpdp 了,定义 dpdpdp 数组为:dp[i][j]dp[i][j]dp[i][j],表示前i个物品中必选 a[i]a[i]a[i], a[i]a[i]a[i] 为最大值,且体积为 jjj 的方案数。
转移方程为:dp[0][0]=1,dp[i][j]=∑dp[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....i1][j],复杂度 O(n2)O(n^2)O(n2)

(4) 求取答案,Ans=∑dp[i][j]∗max(a[i],ceil(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、题解:

非常妙的一道题,直接开始推导:
假设我们划分 aaa 个’L’形,bbb 个2*2连通块。
我们可以得到一个方程组:
TTT 是一个未知的非负整数,满足 a∗3+b∗4=Ta * 3 + b * 4 = Ta3+b4=T
b=(T−3∗a)/4b=(T{-}3*a)/4b=(T3a)/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/4f(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/4f(a,b)=f(a,(T3a)/4)=ax+by=ax+(T3a)/4y=(x3y/4)a+Ty/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; 
}

三、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嘗_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值