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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步