HDU-6377度度熊看球赛(dp)

本文探讨了一个有趣的组合数学问题——世界杯期间情侣们如何随机就座才能最小化影响观看体验的期望喧闹值,并提供了一种基于动态规划的解决方案。

题目

世界杯正如火如荼地开展!度度熊来到了一家酒吧。

有 N 对情侣相约一起看世界杯,荧幕前正好有 2×N 个横排的位置。

所有人都会随机坐在某个位置上。

当然,如果某一对情侣正好挨着坐,他们就会有说不完的话,影响世界杯的观看。

一般地,对于一个就座方案,如果正好有 K 对情侣正好是挨着坐的,就会产生 DK 的喧闹值。

度度熊想知道随机就座方案的期望喧闹值。

为了避免输出实数,设答案为 ans,请输出 ans×(2N)! mod P 的值。其中 P=998244353

思路:dp[i][j]表示已有i对情侣入座,j对情侣坐在一起的方案数。

for(int i=1;i<=N;i++)
    for(int j=0;j<=i;j++)
    {
        ///第i对情侣不分开
        dp[i][j]+=dp[i-1][j]*j;///坐在了之前挨着坐的情侣的中间
        dp[i][j]+=dp[i-1][j-1]*(2*(i-1)-(j-1)+1);///把之前的(j-1)对情侣看成一团,放在他们的间隙插空坐
          
        ///第i对情侣分开
        dp[i][j]+=dp[i-1][j+2]*((j+2)*(j+1)/2);///两个人分别拆散一对情侣
        dp[i][j]+=dp[i-1][j+1]*(2*(i-1)-(j+1)+1)*(j+1);///其中一个人拆散了一对情侣
        dp[i][j]+=dp[i-1][j]*((2*(i-1)-j+1)*(2*(i-1)-j)/2);///两个人都没对其他情侣动手
    }

完整代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=1000;
ll dp[N+5][N+5],p[N+5];
void init()
{
    p[0]=1ll;
    for(int i=1;i<=N;i++)
        p[i]=p[i-1]*2ll%mod;///情侣之间也有座位先后顺序,预处理出2^n
    dp[0][0]=1;
    for(ll i=1;i<=N;i++)
        for(ll j=0;j<=i;j++)
        {
            ///第i对情侣不分开
            dp[i][j]+=dp[i-1][j]*j%mod;
            if(dp[i][j]>mod)
                dp[i][j]-=mod;
            if(j>0)
            {
                dp[i][j]+=dp[i-1][j-1]*(2*(i-1)-(j-1)+1)%mod;
                if(dp[i][j]>mod)
                    dp[i][j]-=mod;
            }

            ///第i对情侣分开
            dp[i][j]+=dp[i-1][j+2]*((j+2)*(j+1)/2)%mod;
            if(dp[i][j]>mod)
                dp[i][j]-=mod;
            dp[i][j]+=dp[i-1][j+1]*(2*(i-1)-(j+1)+1)*(j+1)%mod;
            if(dp[i][j]>mod)
                dp[i][j]-=mod;
            dp[i][j]+=dp[i-1][j]*((2*(i-1)-j+1)*(2*(i-1)-j)/2)%mod;
            if(dp[i][j]>mod)
                dp[i][j]-=mod;
        }
}
int n;
ll d;
int main()
{
    init();
    while(~scanf("%d%lld",&n,&d))
    {
        ll ans=0,tmp=1ll;
        for(int i=0;i<=n;i++) ///O(n)累加算结果
        {
            ans+=dp[n][i]*tmp%mod;
            if(ans>mod) ans-=mod;
            tmp=tmp*d%mod;
        }
        ans=ans*p[n]%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

 

HDU-3480 是一个典型的动态规划问题,其题目标题通常为 *Division*,主要涉及二维费用背包问题或优化后的动态规划策略。题目大意是:给定一个整数数组,将其划分为若干个连续的子集,每个子集最多包含 $ m $ 个元素,并且每个子集的最大值与最小值之差不能超过给定的阈值 $ t $,目标是使所有子集的划分代价总和最小。每个子集的代价是该子集最大值与最小值的差值。 ### 动态规划思路 设 $ dp[i] $ 表示前 $ i $ 个元素的最小代价。状态转移方程如下: $$ dp[i] = \min_{j=0}^{i-1} \left( dp[j] + cost(j+1, i) \right) $$ 其中 $ cost(j+1, i) $ 表示从第 $ j+1 $ 到第 $ i $ 个元素构成一个子集的代价,即 $ \max(a[j+1..i]) - \min(a[j+1..i]) $。 为了高效计算 $ cost(j+1, i) $,可以使用滑动窗口或单调队列等数据结构来维护区间最大值与最小值,从而将时间复杂度优化到可接受的范围。 ### 示例代码 以下是一个简化版本的动态规划实现,使用暴力方式计算区间代价,适用于理解问题结构: ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 0x3f3f3f3f; const int MAXN = 10010; int a[MAXN]; int dp[MAXN]; int main() { int T, n, m; cin >> T; for (int Case = 1; Case <= T; ++Case) { cin >> n >> m; for (int i = 1; i <= n; ++i) cin >> a[i]; dp[0] = 0; for (int i = 1; i <= n; ++i) { dp[i] = INF; int mn = a[i], mx = a[i]; for (int j = i; j >= max(1, i - m + 1); --j) { mn = min(mn, a[j]); mx = max(mx, a[j]); if (mx - mn <= T) { dp[i] = min(dp[i], dp[j - 1] + mx - mn); } } } cout << "Case " << Case << ": " << dp[n] << endl; } return 0; } ``` ### 优化策略 - **单调队列**:可以使用两个单调队列分别维护当前窗口的最大值与最小值,从而将区间代价计算的时间复杂度从 $ O(n^2) $ 降低到 $ O(n) $。 - **斜率优化**:若问题满足特定的决策单调性,可以考虑使用斜率优化技巧进一步加速状态转移过程。 ### 时间复杂度分析 原始暴力解法的时间复杂度为 $ O(n^2) $,在 $ n \leq 10^4 $ 的情况下可能勉强通过。通过单调队列优化后,可以稳定运行于 $ O(n) $ 或 $ O(n \log n) $。 ### 应用场景 HDU-3480 的问题模型可以应用于资源调度、任务划分等场景,尤其适用于需要控制子集内部差异的问题,如图像分块压缩、数据分段处理等[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值