2022/8/17日测试(内含金字塔,斗地主,网络连接,X-Magic Pair,X-Magic Pair)
前言
写这篇题解的时候我已经准备好退役了,由于本人学艺不精,已无力继续。
正文
LINK:X-Magic Pair
标签:思维,数学,推公式
在每一步设a>b,则转移是这样的:(a,b) -> ( a - b,b ),(a, a - b) -> 右边:(a, b), ( b,a-b)
一个走回去了,一个和左边一样。所以多于每一个,只有(a, b)->(a - b, b) 一种法则。
那么相当于一直让 a 减去 b,直到 a
Code
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { ll t; scanf("%lld",&t); while(t--) { ll flag=0; ll x, y, k; cin >> x >> y >> k; if (x > y) swap(x, y); while(x) { if (k == x || k == y) { cout << "YES\n"; flag=1; break; } y -= x; if (x > y) swap(x, y); } if(!flag) cout << "NO\n"; } return 0; }
最多的个数
标签:RMQ
用一个数组f[i]记录每一个数据出现的次数。再对于f做RMQ。
对于查询的每一区间,可以查这个区间里的最大值。可是这样显然是不对的。
对于这种情况,查询到的应该是3,可是答案很显然是2。可以发现,对于每一个区间,可以分为三种情况
①:最左边的不完全元素。可以先排除这一部分
②:中间的完全元素部分,对于这一部分直接求最大值即可
③:最右边的不完全部分。这种情况其实不影响,可以融合在情况②中
综上,可以先求②③这一部分的最大值,然后单独看最左边元素的个数与②③这一部分的最大值谁更大一些
Code
#include <bits/stdc++.h> using namespace std; int num[100010], f[100010], MAX[100010][20]; int n; void ST() { int i, j, k; for (i = 1; i <= n; i++) MAX[i][0] = f[i]; k = log((double)(n + 1)) / log(2.0); for (j = 1; j <= k; j++) for (i = 1; i + (1 << j) - 1 <= n; i++) MAX[i][j] = max(MAX[i][j - 1], MAX[i + (1 << (j - 1))][j - 1]); } int rmq_max(int l, int r) { if (l > r) return 0; int k = log((double)(r - l + 1)) / log(2.0); return max(MAX[l][k], MAX[r - (1 << k) + 1][k]); } int main() { int q, i, a, b; while (scanf("%d", &n) && n) { scanf("%d", &q); for (i = 1; i <= n; i++) { scanf("%d", &num[i]); if (i == 1) { f[i] = 1; continue; } if (num[i] == num[i - 1]) f[i] = f[i - 1] + 1; else f[i] = 1; } ST(); for (i = 1; i <= q; i++) { scanf("%d%d", &a, &b); int t = a; while (t <= b && num[t] == num[t - 1]) t++; int cnt = rmq_max(t, b); int ans = max(t - a, cnt); printf("%d\n", ans); } } return 0; }
金字塔
标签:区间DP
在题目中要求了最终要回到起点,所以其中一定会有重复的两种颜色,所以我们需要找到颜色相同的两个房间。
首先,我们先枚举区间,如图,我们首先找到了第一次重复出现的房间,也就是现在的根节点以及下个节点。(因为Graph Editor的某些原因,我就在节点后添上了序号)如图所示
有两种情况,①这两个点是一个;②两个点不是一个,所以,我们进行状态转移时也是要分两种情况分类讨论。
现在我们知道了思路,但是,方案数怎么计算呢?
我们要算相同节点之间的方案总数,只需要对中间节点进行枚举,把中间点的方案数相加即可。
还是一个问题到底怎么算?
学过加法原理和乘法原理的人都知道,将左部分的方案数乘上右部分的方案数便是总的方案数,所以便有状态转移方程:
f [ l ][ r ] = f [ l ][ r ] + f [ l + 1][ k - 1] * f [ k ][ r ];
Code
#include<cstdio> #include<cstring> using namespace std; const long long Mod = 1e9; char s[305]; long long f[305][305]; int main(){ scanf("%s",s+1); int n = strlen(s+1); for(int i = 1;i <= n;i++) f[i][i] = 1; for(int len = 3;len <= n;len++){ for(int l = 1;l+len-1 <= n;l++){ int r = l+len-1; if(s[l] != s[r]){ //首尾都不一样很显然不满足条件 f[l][r] = 0; continue; } f[l][r] = f[l+1][r-1]; for(int k = l+2;k <= r-2;k++) if(s[l] == s[k]) f[l][r] = (f[l][r]+f[l+1][k-1]*f[k][r]%Mod)%Mod; } } printf("%d",f[1][n]); return 0; }
斗地主
标签:模拟
恶心的模拟
为了尽可能的简化我们对题目的描述,我们需要思考,在题目当中,有哪些内容是可以合并或者是可以优先选取的(需要注意, 我们的目标是尽可能快的出完手牌)。
1、因为双王在我们的手牌里不和其他任意牌形成组合,因此,我们可以把它当对子处理。
2、因为我们所有的出牌方式和这张牌的花色都没有关系,因此,不需要去管这个牌的花色是多少。
3、为了让我们的牌更符合常规(大小顺序为3<4<5<…<A<2 ),因此我们将 记A为12 ,2 记为 13,大小王记为14 (当然,这个可以不单独记),其他数全部减 2(包含 J,Q ,K ),方便后续查找
4、除顺子外,其他的牌是出得越多越好(因为不会相互影响)且有时会存在不出顺子比出顺子更好的情
况。
5、在不出顺子的情况下,出牌的优先级为:四带两对->四带二->三带二->三带一->炸弹->三张牌->对子
牌->单张牌
6、将顺子全部排除出来,计算非顺子牌出牌的次数有多少(注意记录那些可以带的牌的数量),然后,用DFS去搜索,究竟是用这个顺子的答案更优还是不用这个顺子的答案更优。
7、注意炸弹也可以看成是两个对子组成的。因此,四带两对也可以使用四带四。
首先,我们要把问题转化尽量容易处理,既具有一般性:那么我们可以把双王看作一副对牌,无视颜色,以及将k设为11,k设为12(其它依照题中所给顺序依次类推)等。
不难想到,我们要用最少的次数出完所有牌,那么对于非顺子的牌,当然是出的越多越好。
如有四张,就不要分成3张或两张来出,那样只会增加次数,对于三张和两张亦然。
同样的,如果可以带牌,当然要尽量选择减少次数多的来带,(注意不是排数最多的,因为我们最终的目标是用最少的次数出完牌)
例如四张的话,优先选择带两张单牌或两对对牌,这样均可减少两次次数,是在不行再选把一副对牌拆看,当单牌来带,这样只能减少一次。
非顺子牌利用贪心的思想解决了,那么顺子呢?
容易想到,顺子的选取是会对其它出牌方式产生影响的,因此我们可以采取爆搜(dfs+回溯)的方法搜顺子,在各种出顺子的方案中记录最小的即可。
为了提高效率,期间当然可采取最优性剪枝来优化。
总结:通过该题不难看出,爆搜的题有一个特点,即需要利用dfs处理出结果,然后通过回溯进行多种解的比较,从中选出最优。
Code
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int T, n, ans, s[15]; void dfs(int shunzi) { if (shunzi > ans) return; //最优化剪枝 int i, j, s1 = 0, s2 = 0, s3 = 0, s4 = 0, cnt = 0; //非顺子 for (i = 1; i <= 14; i++) //单牌需出的次数 if (s[i] == 1) s1++; for (i = 1; i <= 14; i++) //对牌需出的次数 if (s[i] == 2) s2++; for (i = 1; i <= 14; i++) //三带 { if (s[i] == 3) { s3++; if (s1 >= 1) s1--; //三带优先带单 else if (s2 >= 1) s2--; //其次带双 } } for (i = 1; i <= 14; i++) //四带 { if (s[i] == 4) { if (cnt) cnt--; else { s4++; if (s1 >= 2) s1 -= 2; //四代优先带两个单 else { if (s2 >= 2) s2 -= 2; //其次是带双 else if (s2 >= 1) s2--; //最后带单 else cnt++; } } } } //取当前最优解 ans = min(ans, shunzi + s1 + s2 + s3 + s4); for (i = 1; i <= 8; i++) //单顺起点 { for (j = i; j <= 12; j++) //单顺终点 { s[j]--; if (s[j] < 0) break; if (j - i >= 4) dfs(shunzi + 1); } if (j == 13) j--; while (j >= i) //回溯 { s[j]++; j--; } } for (i = 1; i <= 10; i++) //双顺起点 { for (j = i; j <= 12; j++) //双顺终点 { s[j] -= 2; if (s[j] < 0) break; if (j - i >= 2) dfs(shunzi + 1); } if (j == 13) j--; while (j >= i) //回溯 { s[j] += 2; j--; } } for (i = 1; i <= 11; i++) //三顺 { for (j = i; j <= 12; j++) { s[j] -= 3; if (s[j] < 0) break; if (j - i >= 1) dfs(shunzi + 1); } if (j == 13) j--; while (j >= i) { s[j] += 3; j--; } } return; } int main() { scanf("%d%d", &T, &n); while (T--) { memset(s, 0, sizeof(s)); int i, a, b; ans = 54; for (i = 1; i <= n; i++) { scanf("%d%d", &a, &b); if (a == 0) s[14]++; // s统计数量,14表示王牌 if (a == 1) s[12]++; // A相当于12 if (a == 2) }
网络连接
标签:模拟
Code
#include<bits/stdc++.h> using namespace std; int n; int tot; string q[10005]; int ans[10005]; bool check(string s,int len) { bool flg=0; int bj1=0,bj2=0; int cnt=0; long long p[105],q[105]; memset(p,0,sizeof(p)); for(int i=0;i<=100;++i) q[0]=-1; for(int i=0;i<len;++i) { if(bj2==1&&bj1<3) return 0; if(cnt+1<=bj1+bj2) return 0; if(s[i]=='0') { if(flg==0) cnt++; q[cnt]=0; if(i>=0) { if(flg==0&&((s[i-1]<='9'&&s[i-1]>='0')||(s[i+1]<='9'&&s[i+1]>='0'))) { return 0; } } else { if(flg==0&&(s[i+1]<='9'&&s[i+1]>='0')) { return 0; } } flg=1; p[cnt]*=10; } else if(s[i]=='-') return 0; else if(s[i]=='.') { bj1++; flg=0; } else if(s[i]==':') { bj2++; flg=0; } else if(s[i]>='1'&&s[i]<='9') { if(flg==0) cnt++; q[cnt]=0; p[cnt]=p[cnt]*10+s[i]-'0'; flg=1; } else return 0; } if(p[1]<=255&&p[2]<=255&&p[3]<=255&&p[4]<=255&&p[5]<=65535&&cnt==5&&bj1==3&&bj2==1&&q[1]>=0&&q[2]>=0&&q[3]>=0&&q[4]>=0&&q[5]>=0)//奇奇怪怪的判断 return 1; else return 0; } int main() { // freopen("network.in","r",stdin); // freopen("network.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) { string op,s; cin>>op>>s; if(!check(s,s.length())) { printf("ERR\n"); continue; } bool flg=0; if(op=="Server") { for(int j=1;j<=tot;++j) { if(s==q[j]) { flg=1; break; } } if(flg==0) { printf("OK\n"); q[++tot]=s; ans[tot]=i; } else { printf("FAIL\n"); } } else { for(int j=1;j<=tot;++j) { if(q[j]==s) { printf("%d\n",ans[j]); flg=1; break; } } if(flg==0) { printf("FAIL\n"); } } } return 0; }
本文来自博客园,作者:Doria_tt,转载请注明原文链接:https://www.cnblogs.com/pangtuan666/p/16595714.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现