• noip模拟赛 圆桌游戏


    【问题描述】
    有一种圆桌游戏是这样进行的:n个人围着圆桌坐成一圈,按顺时针顺序依次标号为1号至n号。对1<i<n的i来说,i号的左边是i+1号,右边是i-1号。1号的右边是n号,n号的左边是1号。每一轮游戏时,主持人指定一个还坐在桌边的人(假设是i号),让他向坐在他左边的人(假设是j号)发起挑战,如果挑战成功,那么j离开圆桌,如果挑战失败,那么i离开圆桌。当圆桌边只剩下一个人时,这个人就是最终的胜利者。
    事实上,胜利者的归属是与主持人的选择息息相关的。现在,你来担任圆桌游戏的主持人,并且你已经事先知道了对于任意两个人i号和j号,如果i向j发起挑战,结果是成功还是失败。现在你想知道,如果你可以随意指定每轮发起挑战的人,哪些人可以成为最终的胜利者?

    【输入】
    第一行包含一个整数n,表示参加游戏的人数;
    接下来n行,每行包含n个数,每个数都是0或1中的一个,若第i行第j个数是1,表示i向j发起挑战的结果是成功,否则表示挑战结果是失败。第i行第i列的值一定为0。

    【输出】
    一行,包含若干个数,表示可能成为最终胜利者的玩家的标号。标号按从小到大的顺序输出,相邻两个数间用1个空格隔开。

    【输入输出样例1】
    game.in 
    3
    0 1 0
    0 0 1
    0 1 0

    game.out

    1 3


    【输入输出样例1说明】
    先指定2号向3号发起挑战,3号离开;再指定1号向2号发起挑战,2号离开。此时1号是最终胜利者。
    先指定1号向2号发起挑战,2号离开;再指定1号向3号发起挑战,1号离开。此时3号是最终胜利者。
    无论如何安排挑战顺序,2号都无法成为最终胜利者。

    【数据规模与约定】
    对于30%的数据,n≤7
    对于100%的数据,n≤100

    分析:我一开始一直以为这道题是一道图论题,万万没想到竟然是dp题??先破环成链,接下来考虑怎么设计状态.状态不能表示最后活下来的是谁,因为这不是很好转移,那么将答案放到状态的定义中,判断可行性.由于破环成链,n<=100,最后的复杂度肯定是O(n^3),对应的dp解法就是区间dp喽.本来状态设计为f[i][j]表示i到j中最后存活的是谁,等价转换一下,可以变成f[i][j][k]表示i到j中k最后存活可不可以.这样的复杂度就是O(n^4)了,因为有3维了,考虑怎么省掉一维.最后是只有一个人存活下来了,因为是破环成链了,所以可以转化为第i与第i+n个人相邻,也就是相当于最后两个相同的人合并到一起了,那么状态可以设计为f[i][j]表示i与j是否能够相邻,枚举i,j的中间点k,如果f[i][k] && f[k][j],这时只要k能出局就好了.如果i能打败k或者k不能打败j就f[i][j] = 1,至此,这道题就做完了.

    做完这道题不禁让我想起了几个哲学问题:为什么是区间dp?状态为什么要这么设计?为什么第i个人和第i+n个人可以站在一起?其实最主要的还是遇到环要破环成链这一步,结合数据范围还是有可能想到解法的,真是奇妙的一题.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int a[210][210], n, f[210][210];
    
    int main()
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
            {
                scanf("%d", &a[i][j]);
                a[i + n][j + n] = a[i + n][j] = a[i][j + n] = a[i][j];
            }
        for (int i = 1; i <= n * 2 - 1; i++)
            f[i][i + 1] = 1;
        for (int len = 2; len <= 2 * n; len++)
            for (int i = 1; i + len - 1 <= 2 * n; i++)
            {
                int j = i + len - 1;
                for (int k = i + 1; k <= j - 1; k++)
                    if (f[i][k] && f[k][j])
                    {
                        if (a[i][k] || !a[k][j])
                            f[i][j] = 1;
                    }
            }
        for (int i = 1; i <= n; i++)
            if (f[i][i + n])
                printf("%d ", i);
    
        return 0;
    }
  • 相关阅读:
    聚类分析理论之TwoSteps理论篇+实践篇【转载】
    游戏运营杂谈之——>怎么做一份数据日报【转载】
    名词解释CPC、CPM、CPA.......【转载】
    小白学数据分析>数据指标 累计用户数的使用
    Clementine的源节点学习【转载】
    在clementine基于两步聚类算法的次日留存分析探索【转载】
    百度联盟与Google AdSense的比较【转载】
    SAS:商业智能从BI走向BA
    11、位段
    3、SHELL控制语句
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7731737.html
Copyright © 2020-2023  润新知