2018网络赛(附后三题详解)
第一次参加校赛,相比于之前的网络赛,感觉这次难度还行,不算难也不算简单。
先简单说说前面几道题吧
A 水题,找规律
B 水题,最小公倍数
C 稍稍有点难度,先排序,仔细考虑即可
D 水题,找规律
E bfs打表
F 矩阵快速幂递推
G 找到一一对应的方法bfs
好了,仔细说说最后三题吧。
H
一道看上去很像dp的题,但实际上并不是dp,首先发现这个n挺大的,所以n方的算法肯定不用考虑了,在网上找了很长时间资料,才找到一个找斜率最大的算法,顿时感到豁然开朗,原来被蒙骗了,这道题根本就不是dp。先说说找斜率最大的方法,只要找所有相邻两点中斜率最大的值就可以了,为什么,用反证法。假如斜率最大不在相邻的点,那么考虑这个最大斜率的两点连线,发现无论中间的点在上面还是下面,都存在一个中间点和端点的连线斜率不小于这条边,于是矛盾。但最小能不能这么找呢?发现直接找肯定是错的,但是只要稍稍改变一下算法即可,首先可以把这些点看成是xy平面的点,那么如果我们把xy连个值互换的话斜率最大的线就是另一个里面斜率最小的线了(因为互为倒数),所以我们只要把这些点按照体重排序,然后再求斜率最大即可。顺便一说,由于这个是倒数关系用double误差会很大所以建议用int代表分子分母的pair表示分数。
贴代码
# include <stdio.h>
# include <algorithm>
using namespace std;
const int MAX_N = 1e6 + 10;
typedef long long ll;
typedef pair<ll , ll> P;
P A[MAX_N];
int N;
int main()
{
while(~scanf("%d", &N))
{
int i;
for(i = 0 ; i < N ; i++)
{
scanf("%lld", &A[i].first);
A[i].second = i;
}
sort(A , A + N);
P ans = P(1 , 0);
for(i = 1 ; i < N ; i++)
{
ll p = A[i].first - A[i - 1].first;
ll k = A[i].second - A[i - 1].second;
if(k < 0)
k *= -1;
if(p * ans.second < k * ans.first)
ans = P(p , k);
}
printf("%.2lf\n", (double)ans.first / ans.second);
}
return 0;
}
I
个人感觉最难的一道题,思考了好久,不过不知道为什么突然灵光一现想出了方法,这也让我认识到了acm中的概率问题不能一味的用数学方法思考,要学会计算机的想法。当时在网上搜索概率的表示方法,直接吐血,不但各种人自说自话,有些甚至荒唐可笑,让我根本没办法相信哪个,感觉是一个非常深奥的问题,不过这些都不要紧,因为我现在其实也没搞清楚用数学怎么表示概率,这完全是一道概率dp的递推题。
说说思想,考虑一些情况,首先如果新增一个人会怎么样,如果之前已经有三个人的日期了,那无论他加在哪堆人里还是三个人,所以其实不用管具体情况的了,只需要考虑那些只存在1个或2个人在相同日期的情况,然后把他们变成三个人的情况加过去即可(也就是说三个人的情况是单增的),所以用dp[i][j]表示i个1人日期和j个两人日期的情况(另外根据估计,答案人数肯定比总日期数少,所以不需要关心ij之和大于N的情况),这样只要不断递推下去就可以知道结果了。
贴代码
# include <stdio.h>
# include <string.h>
const int MAX_N = 1000;
double dp[MAX_N + 1][MAX_N + 1];
int main()
{
int N;
while(~scanf("%d", &N))
{
int i = 0, j, k;
double ans;
memset(dp , 0 , sizeof(dp));
dp[0][0] = 1;
ans = 0;
while(1)
{
for(j = 0 ; 2 * j <= i ; j++)
{
k = i - 2 * j;
dp[j][k + 1] += (double)(N - j - k) / N * dp[j][k];
if(k)
dp[j + 1][k - 1] += (double)k / N * dp[j][k];
ans += (double)j / N * dp[j][k];
}
i++;
if(ans >= 0.5)
break;
}
printf("%d\n", i);
}
return 0;
}
J
xdoj压轴题,感觉难度仅次于I,也是一道概率题,不过其实相比于I题,这道题并不是那么考验计算机算法的功力,主要还是数学的想法。考虑现在是i时被消除的次数的期望,这个期望可以通过一个等式来表示,只需要等概率地考虑模的数字即可,比如在4个数字时p(1) = 1 / 4 * (0(模一) + p(1)(模二) + p(1) (模三) + p(1)(模四)),也就是说这是一个方程组,n个等式和n个未知量,肯定是有解的,所以用高斯消元法肯定可以解决,但是仔细发现其实更加简单,因为对于计算p(b)时,等式后面出现的p(k)中的k不可能比b大(因为取模),所以直接一个一个解即可。
贴代码
# include <stdio.h>
const int MAX_N = 500;
int y[MAX_N + 1][MAX_N + 1];
double p[MAX_N + 1];
int main()
{
int i, j;
for(i = 1 ; i <= 500 ; i++)
{
for(j = 1 ; j <= 500 ; j++)
y[i][j] = i % j;
}
int N;
while(~scanf("%d", &N))
{
int chu;
double g;
for(i = 1 ; i <= N ; i++)
{
chu = N, g = N;
for(j = 1 ; j <= N ; j++)
{
if(y[i][j] == i)
chu--;
else
g += p[y[i][j]];
}
p[i] = g / chu;
}
double ans = 0;
for(i = 1 ; i <= N ; i++)
ans += p[i];
//printf("%.2lf %.2lf %.2lf %.2lf\n", p[1], p[2], p[3], p[4]);
printf("%.2lf\n", 1 + ans / N);
}
return 0;
}
总结一下吧
网络赛本身时间很长,而且还可以在网上查阅资料,本身难度并不高,即使把网络赛搞定了现场赛也依然是一个艰巨的任务,可能现场赛结束的时候会写一篇总结吧。