希望更好的阅读体验?点这里
题目:
描述
小A在寻找Z神犇时迷路了,这时他发现有一群人正围着桌子进行一种奇特的游戏,通过观察小A发现游戏是这样进行的:他将人按顺时针编号为 1 ∼ n 1 \sim n 1∼n ,每次会有两个相邻的人进行某种奇特游戏的决斗,输的人将退出游戏,同时与输者相邻的另一人将与赢者相邻,譬如 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+1≤k≤j−1 。
注意:读入时因为是二维数组, 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;
}
代码好简单就是想不出来