曾记否,到中流击水,浪遏飞舟。|

Moyyer_suiy

园龄:2年8个月粉丝:4关注:18

2023.2.21

今天下午是模拟赛,机房里两位大佬 AK 了,咱是爆零。所以好好学习天天向上继续努力......


今日比赛题目:(洛谷)P1521 求逆序对(进阶版是 P2513 逆序对数列)、P2511 木棍分割、石子合并(网上没找到,大概是经典石子合并的改版,和 P2511 的思想相似)

另:N老师说今天练的是前缀和优化 dp,但我们(我)连暴力 dp 都不会写。。所以以后需要练习一下暴力!!!不要看到不会的就放弃,焦虑的坐一下午也不太好...


1.洛谷:P1521 求逆序对

1.一个暴力思想(能获得 30pts 的好成绩,总比爆零好对吧):将序列的所有全排列计算出来,直接枚举得出符合条件的答案。这里需要用到一个全排列的函数 next_permutation。然后我就发现我之前的全排列都是手写,不会用这个函数这件事。

存一下直接用函数 next_permutation 生成全排列的代码。

1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int a[10];
 4 int main(){
 5     int n,i,j=1,k;
 6     cin>>n;
 7     for(i=1;i<=n;i++) {a[i]=n-i+1;j*=i;}
 8     for(i=1;i<=j;i++){
 9         next_permutation(a+1,a+n+1);
10         for(k=1;k<=n;k++) cout<<" "<<a[k];
11         cout<<endl;
12     }
13     return 0;
14 }

2.所以这是本题 20pts 的代码(有的暴力是 30pts,我也不知道区别在哪,就自己看看吧)。另外还是,N老师说得对,如果你想得分暴力能得部分分,就是态度的问题...即使手写个全排列也行呜呜呜,事后诸葛不说了,咱也长个教训。

#include<bits/stdc++.h>
using namespace std;
int n,k;
long long ans=0;
int a[1010];
int main(){
    cin>>n>>k;
    int q=1;
    for(int i=1;i<=n;i++){
        a[i]=i;
        q*=i;
    }
    for(int i=1;i<=q;i++){
        next_permutation(a+1,a+n+1);
        int tot=0;
        for(int x=1;x<=n;x++)
            for(int y=x+1;y<=n;y++)
                if(a[x]>a[y]) tot++;
        if(tot==k) ans++;
    }
    cout<<ans%10000;
}

upd on 3.6: 太忙了一直没整理完,今天写一下捏。最后本题我看的题解,脑子动不了了,分析一下。

3.重点:枚举插入数位置 + 前缀和优化。

本题用 dp,我们注意到将状态设置为目前数字个数、需要形成的逆序对个数,会比较好转移,选数和逆序对个数不难发现具有一定关联性。

用 f[i][j] 一个两维的状态表示:选了前 i 个数,需要构成 j 个逆序对的序列方案有几种。可以枚举最后一个数字新选的这一个数字插入的位置:可以在n、n - 1、n - 2、...、1,在前面的排序中,找到位置依次放入新选的数字,从而它会影响到后面的序列,那么插入后逆序对分别构成的个数为:0、1、2、...、n。

状态转移方程由前面所选决定,则方程为:f[i][j] = (f[i - 1][j - q] + f[i][j])。i - 1 即前面序列的排序情况,j - q 在原序列中枚举逆序对的个数。(这里有一点绕,思考了挺久:枚举原先已构成的逆序对个数 j - q,插入 i 后又会生成逆序对 q 个,那么总共就是所求的 j 个逆序对。这里用前缀和优化统计前面每种情况的逆序对个数。)感觉还是非常巧妙的,做题的时候应好好考虑一下 第 i 个和前 i - 1 序列的关系:是不是能直接加上什么?另外注意这 j 个逆序对是怎么来的,前面的转换过来和后面加上的?然后稍稍优化。反正做题的时候切记别忘了写暴力,不会写就多练,通过暴力往往能想出正解,同时可以注意一下 dp 问题用样例感受前后子问题的阶段转移性。

另外关于膜数,注意需要在每一次计算时都膜一下。一定看好题目要求,一开始做的时候因为膜错数 WA 了...

#include<bits/stdc++.h>
using namespace std;
int n, k;
int f[5010][5010];
int main()
{
    scanf("%d%d", &n, &k);
    f[1][0] = f[2][1] = f[2][0] = f[0][0] = 1;
    for(int i = 3; i <= n; i ++ )
        for(int j = 0; j <= k; j ++ )
            for(int q = 0; q <= i - 1 && j - q >= 0; q ++ )
                f[i][j] = (f[i - 1][j - q] + f[i][j]) % 10000;
    printf("%d", f[n][k]);
    return 0;
}

2.洛谷:P2511 木棍分割

诶,紫的,那就先不做了咕咕咕。

本文作者:Moyyer_suiy

本文链接:https://www.cnblogs.com/Moyyer-suiy/p/17142324.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Moyyer_suiy  阅读(29)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起