题目大意:一圈人与相邻人对打,必有一人获胜,问某人是否可以通过更改搏斗顺序获胜。
解题思路:
1.乍一看以为是邻接矩阵遍历,但是不相邻的两个人不一定可以相遇。
2.所以问题变为判断不相邻两人是否可以相遇,第i个人是否能与第j人相遇要满足条件P。
3.令dp[i][j]记录i与j是否相遇,那么i与j相遇的条件P = dp[i][k] && dp[k][j] && (win[i][k] || win[j][k])
4.将i拆分为两个人,如果在循环后i可以与i‘相遇。那么i可以获胜。
// 1822. Fight Club
#include <cstdio>
#include <cstring>
#define MAX 41
#define DPMAX 100
int dp[DPMAX][DPMAX];
int canWin[MAX][MAX];
int m,n;
void DP(){
memset(dp,0,sizeof(dp));
int _2N = 2 * n - 1; // 将n个人拆分为2n个人
for( int i = 0; i < _2N; i++ ){
dp[i][i+1] = 1; // 表示自己可以遇到相邻的人
}
for( int k = 2; k <= n; k++ ){ // 循环判断是否可以遇到自己
for( int i = 0; i <= _2N - k; i++ ){ // 遍历每个点,判断每个点dp,是否可遇
for( int j = i + 1; j < i + k; j++ ){ // 遍历自己与自己相遇中间的每个点
if( dp[i][j] && dp[j][i+k] ){
int tmpi = i % n;
int tmpj = j % n;
int tmpik = ( i + k ) % n;
if( canWin[tmpi][tmpj] || canWin[tmpik][tmpj] ){
dp[i][i+k] = 1;
break;
}
}
}
}
}
}
int main(){
//freopen("D://input.txt","r",stdin);
scanf("%d",&m);
while( m-- ){
scanf("%d",&n);
for( int i = 0; i < n; i++ ){
for( int j = 0; j < n; j++ ){
scanf("%d",&canWin[i][j]);
}
}
DP();
for( int i = 0; i < n; i++ ){
if( dp[i][i+n] )
printf("%d\n",1);
else
printf("%d\n",0);
}
printf("\n");
}
return 0;
}