[区间DP]决斗

9 篇文章 0 订阅

希望更好的阅读体验?点这里

题目:

描述

小A在寻找Z神犇时迷路了,这时他发现有一群人正围着桌子进行一种奇特的游戏,通过观察小A发现游戏是这样进行的:他将人按顺时针编号为 1 ∼ n 1 \sim n 1n ,每次会有两个相邻的人进行某种奇特游戏的决斗,输的人将退出游戏,同时与输者相邻的另一人将与赢者相邻,譬如 1 1 1 2 2 2 决斗, 2 2 2 失败,则之后 1 1 1 3 3 3 相邻。最终剩下的人即为赢家。小A向他们问路,他们要求小A预测谁是最后的赢家,预测成功则告诉小A路。小A经过若干次失败发现这些人之间任意两个人进行决斗的赢家是固定,小A用一张 n × n n \times n n×n 的表来表示,若第 i i i 行与第 j j j 列值为 1 1 1 ,则表示 i i i j j j 决定 i i i 始终胜利,为 0 0 0 则表示失败。当然 i i i j j j 列( i ≠ j i \not = j i=j)数值与 j j j i i i 列数值一定一个为 0 0 0 ,一个为 1 1 1 ,规定第 i i i i i i 列值始终为 0 0 0 。注意: A A A B B B B B B C C C ,不代表 A A A C C C 。小 A A A 知道最终的赢家与比赛顺序有关,但是不是所有人都有可能成为赢家。为提高预测成功率,小 A A A 希望知道哪些人可能为赢家。

输入

第一行两个整数 n n n ,表示 n n n 个人。
2 ∼ ( n + 1 ) 2 \sim (n+1) 2(n+1) 行每行 n n n 个整数表示二维表。

输出

按字典序依次输出赢家编号,以空格隔开。

分析:

由于是环形问题,所以可以把数组扩大一倍,按照直线处理

如果按照题目操作时, i i i i + n i+n i+n 相邻,就说明 i i i 是赢家,因为在 i i i i + n i+n i+n 之间的是除了 i i i 以外的所有人,说明他能打败所有人。

设布尔数组 f [ i ] [ j ] f[i][j] f[i][j] 表示 i i i j j j 是否相邻,这样最后只要判断 f [ i ] [ i + n ] f[i][i+n] f[i][i+n] 是否为真就行了。

初始化: f [ i ] [ i + 1 ] = t r u e f[i][i+1]=true f[i][i+1]=true

如果 i i i 能打败 k k k j j j 能打败 k k k ,并且 i i i k k k j j j k k k 相邻,则 f [ i ] [ j ] = t r u e f[i][j]=true f[i][j]=true 。显然 i + 1 ≤ k ≤ j − 1 i+1 \le k \le j-1 i+1kj1

注意:读入时因为是二维数组, i i i i + n i+n i+n j j j j + n j+n j+n 分别表示一个人,所以要同时把 ( i + n , j ) (i+n,j) (i+n,j) ( i , j + n ) (i,j+n) (i,j+n) ( i + n , j + n ) (i+n,j+n) (i+n,j+n) 四个位置设为 ( i , j ) (i,j) (i,j)

代码:

#include <cstdio>
int n,a[603][603];//一定要开成双倍!否则可能RE
bool f[603][603];//这个也是
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]=a[i][j];
            a[i][j+n]=a[i][j];
            a[i+n][j+n]=a[i][j];
        }
    for(int i=1;i<(n<<1);i++) f[i][i+1]=1;//初始化
    for(int i=(n<<1)-2;i>=1;i--)//枚举左端点
        for(int j=i+2;j<=(n<<1);j++)//枚举右端点
        //其实改成先枚举距离,后枚举左端点也行,只不过枚举左右端点要倒着做
            for(int k=i+1;k<j;k++)//枚举k
                if(f[i][k]&&f[k][j]&&(a[i][k]||a[j][k]))
                    f[i][j]=1;
    for(int i=1;i<=n;i++)
        if(f[i][i+n]) printf("%d ",i);//如果f[i][i+n]为真,说明i是赢家
    return 0;
}

代码好简单就是想不出来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值