NewOJ Week 8题解

2023-10-18 20:59
文章标签 题解 week newoj

本文主要是介绍NewOJ Week 8题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

比赛链接:http://oj.ecustacm.cn/contest.php?cid=1022

题目总览

题目TAG难度来源补题链接
猜数字概率计算 C o d e C h e f CodeChef CodeChefhttp://oj.ecustacm.cn/problem.php?id=1826
升降数字贪心☆☆ P A C N W 2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1827
黑白配期望 D P DP DP、状态压缩 D P DP DP☆☆☆☆ P A C N W 2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1828
最短缺失子序列思维题、贪心、字符匹配☆☆☆☆ P A C N W 2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1829
房间划分计算几何、三角形面积☆☆☆ P A C N W 2021 PACNW \ 2021 PACNW 2021http://oj.ecustacm.cn/problem.php?id=1830

A 猜数字

题意: A l i c e Alice Alice从数字 [ 1 , N ] [1,N] [1,N]中随机选择一个整数作为 x x x B o b Bob Bob从数字 [ 1 , M ] [1,M] [1,M]中随机选择一个整数作为 y y y。求 x + y x+y x+y等于奇数的概率是多少?

Tag: 概率计算

难度:

来源: C o d e C h e f CodeChef CodeChef

思路: 总共存在 N M NM NM种选择情况。要求和为奇数,要么 x x x y y y偶,或者 x x x y y y奇。

可以直接分类讨论求出 [ 1 , N ] [1,N] [1,N]的奇数、偶数数量,也可以根据数学规律计算出 [ 1 , N ] [1,N] [1,N]中的奇数个数为 ⌊ N + 1 2 ⌋ \lfloor \frac{N+1}{2}\rfloor 2N+1,偶数个数为 ⌊ N 2 ⌋ \lfloor \frac{N}{2} \rfloor 2N

最终要对答案进行约分,求出最大公因数即可。

#include<bits/stdc++.h>
using namespace std;
int main()
{int T;cin >> T;while(T--){long long n, m;cin >> n >> m;//奇数:(n + 1) / 2,偶数n / 2long long p = ((n + 1) / 2) * (m / 2) + ((m + 1) / 2) * (n / 2);long long q = n * m;long long g = __gcd(p, q);cout<<p/g<<"/"<<q/g<<endl;}return 0;
}

B 升降数字

题意: 升降数字:一个数字可以被分成两部分(可能有一部分是空的),第一部分属于非递减,第二部分属于非递增。现在给定数字 n n n,请求出不超过 n n n的最大的升降数字。

Tag: 贪心

难度: ☆☆

来源: P A C N W 2021 PACNW \ 2021 PACNW 2021

思路: 对于给定的数字 n n n,直接从前往后找出最远的满足非递减的部分,然后再接着找出最远的满足非递增部分。

例如数字 12345666544342 12345666544342 12345666544342,首先从前往后找非递减部分: 12345666 12345666 12345666;然后接着找非递增, 123456665443 123456665443 123456665443,后面的部分是不满足的部分。

题目要求第一个是要求比数字 n n n小,第二个是要把后半部分变成非递增。则最优情况是将后面所有非法的全部变成最后一个合法的数字

上面的例子也就是 12345666544333 12345666544333 12345666544333

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
char s[maxn];
int main()
{int T;scanf("%d", &T);while(T--){scanf("%s", s);int n = strlen(s);int idx = 0;//非递减部分while(s[idx] <= s[idx + 1] && idx + 1 < n)idx++;//非递增部分while(s[idx] >= s[idx + 1] && idx + 1 < n)idx++;//非法部分for(int i = idx; i < n; i++)s[i] = s[idx];printf("%s\n", s);}return 0;
}

C 黑白配

题意: 黑白配是一个经典的游戏,在每一轮中,孩子们将手朝上(白色)或者朝下(黑色)。
如果所有的孩子做出相同的选择,只有一个例外,那么剩下的孩子将会被淘汰。
游戏重复进行,直到只剩下两个孩子停止。
每个孩子有一个固定的概率独立选择是否将手朝上。
给定 n ( ≤ 20 ) n(\le 20) n(20)个孩子的概率,请输出游戏期望回合数是多少。

Tag: 期望 D P DP DP、状态压缩 D P DP DP

难度: ☆☆☆☆

来源: P A C N W 2021 PACNW \ 2021 PACNW 2021

思路: 初始状态是 n n n个人都参与游戏,终止状态是参与人数等于 2 2 2个人。由于 n n n很小,用二进制数字 x x x表示 n n n个人的淘汰情况。

例如 x = 3 = ( 111 ) 2 x=3=(111)_2 x=3=(111)2表示1、2、3均未淘汰,而 x = 2 = ( 010 ) 2 x=2=(010)_2 x=2=(010)2则表示 1 1 1 3 3 3被淘汰。

d p [ x ] dp[x] dp[x]表示状态为 x x x的期望回合数,在此情况下进行一轮游戏,存在两种可能:

  1. 游戏状态发生改变,即有人淘汰,记概率为 t o t tot tot
  2. 游戏状态未发生改变,即没有人淘汰,概率为 ( 1 − t o t ) (1-tot) (1tot)

那么状态 x x x的期望轮次等于:
E [ x ] = t o t ⋅ E [ y ] + ( 1 − t o t ) ⋅ E [ x ] + 1 E[x]=tot \cdot E[y]+(1-tot)\cdot E[x]+1 E[x]=totE[y]+(1tot)E[x]+1
上式中的 y y y表示 x x x状态淘汰某个人后变成的状态,实际上可能存在 k k k种转移情况:
E [ x ] = ∑ i = 1 k p y i ⋅ E [ x − 2 y i ] + ( 1 − ∑ i = 1 k p y i ) ⋅ E [ x ] + 1 E[x]=\sum_{i=1}^k p_{y_i}\cdot E[x-2^{y_i}] + \left(1-\sum_{i=1}^kp_{y_i}\right)\cdot E[x]+1 E[x]=i=1kpyiE[x2yi]+(1i=1kpyi)E[x]+1
上式的 ∑ i = 1 k p y i \sum_{i=1}^kp_{y_i} i=1kpyi就等于 t o t tot tot,总共存在 k k k种淘汰情况,其中 k k k表示 x x x中1的个数, x x x的第 y i y_i yi位等于1。

y i y_i yi个人淘汰后,状态由 x x x转变成 x − 2 y i x-2^{y_i} x2yi

为了求解 E [ x ] E[x] E[x],将 E [ x ] E[x] E[x]放在一边,用 t o t tot tot简化上式:
t o t ⋅ E [ x ] = ∑ i = 1 k p y i ⋅ E [ x − 2 y i ] + 1 tot\cdot E[x] =\sum_{i=1}^k p_{y_i}\cdot E[x-2^{y_i}]+1 totE[x]=i=1kpyiE[x2yi]+1
这样暴力用二进制枚举求解 E E E数组即可。注意 p y i p_{y_i} pyi表示的是在状态 x x x下第 y i y_i yi个人淘汰的概率,存在两种情况:其他人全白第 y i y_i yi个人黑,或者反过来。

#include<bits/stdc++.h>
using namespace std;
int n;
double p[20];
double dp[(1 << 20) + 10];
//dp[i]表示剩余人数状态为i时的期望轮数inline int bin_count(int x)
{int ans = 0;while(x)++ans, x -= (x & (-x));return ans;
}int main()
{cin >> n;for(int i = 0; i < n; i++)cin >> p[i];for(int x = 0; x < (1 << n); x++){if(bin_count(x) <= 2)continue;//当前状态下所有人出白的概率,出黑的概率double aw = 1.0, ab = 1.0;for(int i = 0; i < n; i++)if(x & (1 << i))aw *= p[i], ab *= (1 - p[i]);//tot表示当前状态x可以转移到其他状态的概率(有人淘汰的概率)double tot = 0.0;for(int i = 0; i < n; i++)if(x & (1 << i)){//pi表示淘汰第i个人的概率double pi = aw / p[i] * (1 - p[i]) + ab / (1 - p[i]) * p[i];dp[x] += pi * dp[x - (1 << i)];tot += pi;}dp[x] = (dp[x] + 1) / tot;}cout<<fixed<<setprecision(12)<<dp[(1 << n) - 1]<<endl;return 0;
}

D 最短缺失子序列

题意:字符串 t t t是字符串 s s s子序列:字符串 s s s删除 0 0 0个或者多个字符可变成字符 t t t
字符串 t t t是字符串 s s s缺失子序列:字符串 t t t不是字符串 s s s的子序列,但是字符串 t t t中出现的字母,均在 s s s中出现过。
字符串 t t t是字符串 s s s最短缺失子序列:字符串 t t t是字符串 s s s的缺失子序列,同时长度是最短的。
现在给定字符串 s s s,询问 m m m次,每次询问一个字符串 t t t是否为 s s s的最短缺失子序列。

Tag: 思维题、贪心、字符匹配

难度: ☆☆☆☆

来源: P A C N W 2021 PACNW \ 2021 PACNW 2021

思路: 首先最短缺失子序列长度是固定的,因此第一步是求出这个长度。

对于仅由前 c c c个字母组成的字符串 s s s,最短缺失子序列长度是多少呢?

例如仅有前 3 3 3个字母组成的字符串 " a b c c b a " "abccba" "abccba",可以发现在其中长度为1的子序列不是缺失子序列,长度为2的子序列 a a , a b , a c , b a , b b , b c , c a , c b , c c aa,ab,ac,ba,bb,bc,ca,cb,cc aa,ab,ac,ba,bb,bc,ca,cb,cc也不是缺失子序列。

什么情况下长度为 i i i的子序列全都不是缺失子序列?

c c c个字母恰好出现 i i i次?更确切的说应该是存在 i i i段,每段包含前 c c c个字母至少 1 1 1次。

例如上述字符串 a b c c b a abccba abccba,可以分成两端 a b c abc abc c b a cba cba,正好 a b c abc abc都出现 1 1 1次,这样长度为 2 2 2的子序列肯定都可以构造出,只要每段选一个字母即可。

再例如字符串 a b c c a b c c a abccabcca abccabcca,最多分成两部分 a b c abc abc c a b c c a cabcca cabcca,每部分 a b c abc abc都至少出现 1 1 1次,说明最短缺失子序列长度为 3 3 3

所以给定字符串 s s s,直接贪心从左往右每次从 l l l开始找一个最靠左的端点 r r r,使得 [ l , r ] [l,r] [l,r]满足前 c c c个字母至少出现1次。找出的段数+1就是最短缺失子序列长度。

求出长度之后,对于每次询问 t t t只需要判断两件事:

1、字符串 t t t的长度是否等于最短缺失子序列

2、 t t t是否不是 s s s的子序列。

判断字符串 t t t是否不是 s s s的子序列,可以利用数组 N e x t [ i ] [ j ] Next[i][j] Next[i][j]在线性时间内判断。

N e x t [ i ] [ j ] Next[i][j] Next[i][j]表示第 i i i个位置之后第 j j j个字母的位置,在输入的时候就可以预处理好。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
char v[30], s[maxn], t[maxn];///Next[i][j]表示第i位后字符'A'+j的位置
int Next[maxn][26];
///Next[0]存储每个字符第一次出现的位置int main()
{scanf("%s", v + 1);scanf("%s", s + 1);int vlen = strlen(v + 1), slen = strlen(s + 1);//求最短缺失子序列长度lenint K = 0, len = 1;for(int i = 1; i <= vlen; i++)K |= (1 << (v[i] - 'A'));int tK = 0;for(int i = 1; i <= slen; i++){tK |= (1 << (s[i] - 'A'));if(tK == K)len++, tK = 0;//对于字符s[i],往前暴力更新Next数组for(int j = i - 1; j >= 0; j--){Next[j][s[i] - 'A'] = i;//直到找到上一个s[i]停止if(s[j] == s[i])break;}}int n;scanf("%d", &n);while(n--){scanf("%s", t + 1);int tlen = strlen(t + 1);int ok = 0;if(tlen == len)//保证最短{int pos = 0;for(int i = 1; i <= tlen; i++){pos = Next[pos][t[i] - 'A'];if(!pos)break;}//pos等于0说明是无法匹配的//此时为缺失子序列ok = (pos == 0);}printf("%d\n", ok);}return 0;
}

E 房间划分

题意: 给定一个凸多边形房间,现在需要将房间划分成等面积的两部分。
房间只有一个门,可以视为一个顶点,需要沿着直线将房间划分成两部分。
直线的一端必须是门,另一端必须是墙角或者墙壁。
如下图所示,最终需要求解出直线另一端点的坐标。

img

Tag: 计算几何、三角形面积

难度: ☆☆☆

来源: P A C N W 2021 PACNW \ 2021 PACNW 2021

思路: 题目相当于给一个凸多边形,找一条直线使得面积平分。直线的一点在第一个端点上,请求出另一个在凸多边形边上的端点。

利用三角形面积公式可以求出凸多边形面积 S = ∑ i = 2 n − 1 S Δ ( p [ 1 ] , p [ i ] , p [ i + 1 ] ) S=\sum_{i=2}^{n-1}S_\Delta(p[1],p[i],p[i+1]) S=i=2n1SΔ(p[1],p[i],p[i+1])

求出 S S S后,求第几个三角形加入后,面积恰好从小于 S 2 \frac{S}{2} 2S变成大于等于 S 2 \frac{S}{2} 2S

假设 Δ ( p [ 1 ] , p [ i ] , p [ i + 1 ] ) \Delta(p[1],p[i],p[i+1]) Δ(p[1],p[i],p[i+1])后恰好满足上述条件,记加入前面积为 S 1 S_1 S1,加入后面积为 S 2 S_2 S2,则在线段 [ p [ i ] , p [ i + 1 ] ] [p[i],p[i+1]] [p[i],p[i+1]]等比例找一个点 x x x满足:
∣ ∣ x − p [ i ] ∣ ∣ ∣ ∣ p [ i + 1 ] − p [ i ] ∣ ∣ = S 2 − S 1 S 2 − S 1 \frac{||x-p[i]||}{||p[i+1]-p[i]||} = \frac{\frac{S}{2}-S_1}{S_2-S_1} p[i+1]p[i]xp[i]=S2S12SS1

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
typedef long long ll;
int n;
struct Point
{double x, y;Point(){}Point(double x, double y):x(x), y(y){}void input(){scanf("%lf%lf", &x, &y);}Point operator -(const Point& a)const{return Point(x - a.x, y - a.y);}Point operator +(const Point& a)const{return Point(x + a.x, y + a.y);}Point operator *(double p)const{return Point(x * p, y * p);}
}node[maxn];
typedef Point Vector;
//叉积
inline double Cross(Vector A, Vector B){return A.x*B.y - A.y*B.x;
}
//三角形面积
inline double Area(Point A, Point B, Point C){return 0.5 * Cross(B-A, C-A);
}int main()
{scanf("%d", &n);for(int i = 1; i <= n; i++)node[i].input();double half_area = 0;for(int i = 2; i <= n - 1; i++)half_area += Area(node[1], node[i], node[i + 1]);half_area *= 0.5;double now_area = 0;for(int i = 2; i <= n - 1; i++){double next_area = now_area + Area(node[1], node[i], node[i + 1]);if(now_area <= half_area && next_area >= half_area){//cout<<i<<" "<<now_area<<" "<<half_area<<" "<<next_area<<endl;//AB = AM + MB//(half_area - now_area) : (next_area - now_area) = AM : ABdouble p = (half_area - now_area) / (next_area - now_area);Point delta = (node[i + 1] - node[i]) * p;Point ans = node[i] + delta;printf("%.9f %.9f\n", ans.x, ans.y);break;}now_area = next_area;}return 0;
}

这篇关于NewOJ Week 8题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/235123

相关文章

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

【秋招笔试】9.07米哈游秋招改编题-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集 100+ 套笔试题,笔试真题 会在第一时间跟新 🍄 题面描述等均已改编,如果和你笔试题看到的题面描述

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

P2858 [USACO06FEB] Treats for the Cows G/S 题解

P2858 题意 给一个数组。每天把最左或者最右的东西卖掉,第 i i i个东西,第 d a y day day天卖出的价格是 a [ i ] ∗ d a y a[i]*day a[i]∗day。 记忆化搜索 void dfs(int l,int r,int day,ll sum){if(v[l][r]>=sum)return;v[l][r]=sum;if(l>r)//这就是dp答案{

【C++题解】1272. 郭远摘苹果

欢迎关注本专栏《C++从零基础到信奥赛入门级(CSP-J)》 问题:1272. 郭远摘苹果 类型:二维数组 题目描述: 郭远有一天走到了一片苹果林,里面每颗树上都结有不同数目的苹果,郭远身上只能拿同一棵树上的苹果,他每到一棵果树前都会把自己身上的苹果扔掉并摘下他所在树上的苹果并带走(假设郭远会走过每一棵苹果树),问在郭远摘苹果的整个过程中,他身上携带的最多苹果数与最小苹果数的差是多少?

【最新华为OD机试E卷-支持在线评测】机器人活动区域(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 94 分 最新华为OD机试目录: https://blog.

2023 CCPC(秦皇岛)现场(第二届环球杯.第 2 阶段:秦皇岛)部分题解

所有题目链接:Dashboard - The 2023 CCPC (Qinhuangdao) Onsite (The 2nd Universal Cup. Stage 9: Qinhuangdao) - Codeforces 中文题面: contest-37054-zh.pdf (codeforces.com) G. Path 链接: Problem - G - Codeforces