YACS 2022年9月月赛 甲组 T3 123型序列 题解

题目链接

前几天都在搞比赛,今天终于有空来写其他的题解了

那就写点甲组的装 b

看了下榜单,最简单的就是 T3 了,我先写个 T3 的题解吧!

这毕竟是一道甲组的题目,于是时间过去了 30 分钟...

一眼就能看出来是一个 DP ,但是状态转移方程和状态设计该怎么弄呢?

首先先来讲一下暴力做法:

暴力

很简单,枚举填什么即可,只能得 30

我们可以考虑优化,优化方法似乎只有一种:剪枝

意思就是如果我发现当前第i个地方我填的数字已经不符合要求了,那么就可以直接 return 掉了

怎么判断不符合要求呢?

首先我们需要把这 m 个要求整理一下,为了判断方便,我们建一个 vector

 v[i] 存的就是以 i 结尾的区间的要求

当我们填完i时,只需要检查 v[i] 这里面的要求是否符合即可

怎么判断要求是否符合呢?

因为需要知道任意区间内有几种数字,所以我们可以多加三个变量 a,b,c

分别表示 1 最后一次填是在哪里,2 最后一次填是在哪里还有 3 最后一次填是在哪里

发现了不合法的就可以直接 return  了

但是剪枝依旧没多大用

DP

如果你能想到上面的剪枝优化的话,就应该能够想到这个正解了

如果想要转移,那么我们需要知道任意区间内出现了多少个数字

一眼看下去时间复杂度应该是 O(n3)

那么就可以设计状态了

我们设 f[i][j][k][l] 为当前已经填完了第i位且满足要求,1,2,3 这三个数字最后一次出现的位置分别是 j,k,l

但是时间复杂度是 O(n4) 会炸咋办?

不难发现,j,k,l 中的一个一定是 i

所以我们改一下状态,设 f[i][j][k] 为将 1,2,3 各自最晚出现位置从大到小排序后为 i,j,k0 代表没有出现

状态转移方程很好想,枚举 1,2,3 分别填在哪一位并且判断一下是否合法即可

另外,如果考虑 f[i][j][k] 是哪几项相加,我们需要在算的时候更新一个 sum 数组

因为我太懒了,所以我们采用前推后的写法,就是考虑 f[i][j][k] 会对哪些东西做贡献

那么第 i+1 项就有三种和第i项有关的填法,可以填和 a[i] 一样的

也可以填和 a[j]  一样的或者 a[k] 一样的,就是 1,2  和 3  

a[i] 时:会从 f[i][j][k] 转到 f[i+1][j][k],因为最后填的数字就和上一个一样

a[j] 时:从 f[i][j][k]f[i][i+1][k],最后一个填 a[j] 的是当前,但是需要注意下大小排序,所以变成了 f[i+1][i][k]

a[k] 时:从 f[i][j][k]f[i+1][i][j]

代码很好写,待会儿再更新,主要是再判断一下是否符合要求就可以了,

还有, vector 要用 pair 存,不然要多开一维,初始 f[1][0][0]=3

代码来啦!

复制代码
 1 #include <vector>
 2 #include <iostream>
 3 #define int long long
 4 using namespace std;
 5 int n,m,ans;
 6 int a[310],f[310][310][310];
 7 vector<pair<int,int> > v[310];
 8 bool isleg(int a,int b,int c)
 9 {
10     for(int i = 0;i < v[a].size();i ++)
11     {
12         int l = v[a][i].first;
13         int cnt1 = v[a][i].second,cnt2 = 1;
14         cnt2 += (b >= l) + (c >= l);
15         if(cnt2 != cnt1) return false;
16     }
17     return true;
18 }
19 signed main()
20 {
21     scanf("%lld%lld",&n,&m);
22     for (int i = 1;i <= m;i++)
23     {
24         int l,r,cnt;
25         scanf("%lld%lld%lld",&l,&r,&cnt);
26         v[r].push_back(make_pair(l,cnt));
27     }
28     f[1][0][0] = 3;
29     for(int i = 1;i <= n;i ++)
30     {
31         for(int j = 0;j < i;j ++)
32         {
33             for(int k = 0;k < max(1LL,j);k ++)
34             {
35                 if(!isleg(i,j,k)) continue;
36                 f[i + 1][j][k] = (f[i + 1][j][k] + f[i][j][k]) % 1000000007;
37                 f[i + 1][i][j] = (f[i + 1][i][j] + f[i][j][k]) % 1000000007;
38                 f[i + 1][i][k] = (f[i + 1][i][k] + f[i][j][k]) % 1000000007;
39                 if(i == n) ans = (f[i][j][k] + ans) % 1000000007;
40             }
41         }
42     }
43     printf("%lld\n", ans);
44     return 0;
45 }
复制代码
posted @   Xy_top  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示