【做题笔记】 P6264 【[COCI2014/2015#3]DOM】

部分分很容易想,是个人大概都能写出 (60) 分。


60pts - 暴力

设一个 (now) 数组,其中 (now_i) 表示第 (i) 个老爷爷现在在看什么电视,一开始 (now_{1..n}) 显然为 (p)(一开始都在看 (p) 频道)。然后在不满足条件的时候,就一直循环,循环内按照 (1) ~ (n) 的顺序更改 (now) 的值,每次在循环末尾查看一下是否所有老人都满足条件即可。

那么什么时候输出 (-1) 呢?我们发现,这些老爷爷喜欢的和不喜欢的频道始终不变,这告诉我们若有解,则一定能在 (n) 轮内(包括第 (n) 轮,“一轮”表示一个老爷爷更改了频道)完成。所以同时设 (cnt) 记录经过几轮,当 (cnt>n) 时就铁定无解啦!

参考代码:

#include <cstdio>

int n, m, p, a[100010], b[100010], now[100010], ans;

int main(void)
{
    scanf("%d%d%d", &n, &m, &p);
    for(int i=1; i<=n; i++)
    {
        scanf("%d%d", &a[i], &b[i]);
        now[i] = p;
    }
    
    bool x = false;
    int cnt = 0; //次数

    while(!x)
    {
        if(cnt > n) 
        {
            printf("-1
");
            return 0;
        }

        for(int i=1; i<=n; i++)
            if(now[i] == b[i])
            {
                ans += 1;
                cnt += 1;
                for(int j=1; j<=n; j++)
                    now[j] = a[i];
                break;
            }

        bool pd = true;
        for(int i=1; i<=n; i++)
            if(now[i] == b[i])
                pd = false; //如果有老爷爷最不喜欢当前的频道就不行
        if(pd) x = true;

    }

    printf("%d
", ans);
    
    return 0;
}

100pts - 正解

我们发现此题有一个很好的性质:只需要最年轻的那个人去更改频道即可!刚刚那个 (60) 分的算法的瓶颈就在于记录了大量冗余的状态(冗余的状态无论何时都是一个算法的瓶颈)。

如果一个老爷爷去更改了频道,那么其他的都会顺着被更改;而那个老爷爷更改的频道一定是他最喜欢的那个!

然后仔细回想上面那个算法:在执行玩“换频道”的操作后,是不是需要去判断是否仍有老爷爷要换频道?那么此时判断时那些老爷爷在看那个频道呢?不就是那个起身去换频道的人最喜欢看的频道嘛!

换而言之,我们将用那个起身去换频道的老爷爷最喜欢看的频道作为重新判断的下标!

我们现在可以开始压缩状态了!设 (lau_b) 表示一些老爷爷(变成了一些,压缩了状态) 不喜欢看 (b) 频道,那么 (b_i) 第一次出现的时候,我们就另】令 (lau_{b_i}=a_i) ,否则不动。这样做的道理是假如某两个老爷爷 (1)(2) ,她们都最不喜欢 (b) 频道,那么我们只需要知道最先出现,也就是最年轻的老爷爷最喜欢哪个频道就好了,因为只有老爷爷 (1) 会去换频道,换好之后老爷爷 (2) 也就满足了条件,不需要再换,那么知道老爷爷 (2) 最喜欢哪个频道也就没有意义啦!

然后我们有这样的算法(下面会解释):

while(lau[p])
{
    p = lau[p];
    ans += 1;
    if(ans > n)
    {
        printf("-1
");
        return 0;
    }
}

首先看循环条件——字面意思好像是“当不喜欢 (p) 频道的老爷爷有喜欢的频道”?好奇怪啊!没关系,继续往下看。利用一开始输入的 (p) 频道,把 (p) 变成 (lau_p) ?估计各位已经明白了:如果还有人不喜欢 (p) 频道,那么就相当于变成最年轻的那位老爷爷最喜欢的频道((lau_p))——那么就像上面所说的一样,我们就要去看看现在是否还有其他老爷爷不满足条件啦!

如果当前的 (lau_p) 等于 (0) ,也就意味着没有任何一个老爷爷不喜欢当前的频道,那么问题是不是就解决了!

完整参考代码:

#include <cstdio>

int n, m, p, a[100010], b[100010], now[100010], ans, lau[1000010];

inline int read()
{
    int s=0,w=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0' , ch=getchar();
    return s*w;
}

int main(void)
{
    n = read();
    m = read();
    p = read();
    for(int i=1; i<=n; i++)
    {
        a[i] = read();
        b[i] = read();
        if(!lau[b[i]]) lau[b[i]] = a[i];
    }

    while(lau[p])
    {
        p = lau[p];
        ans += 1;
        if(ans > n)
        {
            printf("-1
");
            return 0;
        }
    }

    printf("%d
", ans);
    
    return 0;
}
原文地址:https://www.cnblogs.com/BlueInRed/p/12617600.html