xdoj 2018校赛 网络赛

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;
}

总结一下吧
网络赛本身时间很长,而且还可以在网上查阅资料,本身难度并不高,即使把网络赛搞定了现场赛也依然是一个艰巨的任务,可能现场赛结束的时候会写一篇总结吧。

阅读更多
换一批

没有更多推荐了,返回首页