黑书中一道例题:决斗https://www.spoj.pl/problems/MUSKET/
书中的分析:
假设需要判断x是否能赢得整场战斗,把环看成链,x点拆成两个,那么编号为x的人能从中胜出的充分必要
条件是他能与自己“相遇”。这样,在连续几个人的链中,只须考虑头尾两个人能否胜利会师,中间的则不予
考虑。设meet[i][j]记录i和j能相遇,能则为true,否则为false,则问题转化为了是否能找到一个k,使得
i 和 k, k和j均能相遇,而 i或j能打败k。
//3463926 2010-04-04 16:10:26 superbin Musketeers accepted 0.03 2.7M
//技巧将meet加倍,以表示循环的人链
#include <stdio.h>
#include <string.h>
#define NL 110
bool e[NL][NL];
bool meet[NL*2][NL*2];
bool muk[NL];
int main()
{
int T;
int n, sum;
int i, j, k, t;
char s[NL];
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (i=0; i<n; i++) {
scanf("%s", s);
for (j=0; j<n; j++)
if (s[j]=='1') e[i][j] = 1;
else e[i][j] = 0;
}
memset(muk, 0, sizeof(muk));
memset(meet, 0, sizeof(meet));
for (i=0; i<2*n-1; i++)
meet[i][i+1] = 1;
sum = 0;
for (k=2; k<=n; k++) {
for (i=0; i+k<2*n; i++) {
j = i+k;
int flg = 0;
for (t=i+1; t<j; t++) {
if (meet[i][t] && meet[t][j]
&& (e[i%n][t%n] || e[j%n][t%n])) {
flg = 1;
break;
}
}
if (flg) {
meet[i][j] = 1;
if (k==n) {
sum++;
muk[i] = 1;
}
}
}
}
printf("%d\n", sum);
for (i=0; i<n; i++) {
if (muk[i])
printf("%d\n", i+1);
}
}
return 0;
}