题目大意:有n(≤500)盏灯和m(≤500)个开关,每一盏灯有若干个开关控制,只要这若干个开关中有一个处于正确状态(开或者关都可能),这盏灯就会亮。每一个开关最多控制两盏灯。求一种开关的状态使得所有的灯都亮。
分析:一开始想到探索,但是状态太多,又没有想出很好的优化方法,就放弃了(做完之后看题解才知道这是重复覆盖问题,有Dancing Links解法)。又由于前几天在Codeforces上遇到一个题不会做,看别人的代码之后发现可以用随机数的办法来做。因此对这个题也尝试一下,确实可以。
用一个state[]数组保存开关状态,初始状态随意,然后检查所有的灯,维护没亮的灯的集合S。之后重复如下操作:
选一盏没亮的灯,随机选一个控制它的开关改变状态,如果改变这个开关之后有新的灯灭了,加入S。由于每一个开关最多控制两盏灯,因此经过一次操作之后,|S|不会增加。
重复操作直到|S| = 0或达到次数上限。取上限为10万次时,用时31ms。
int swit[501][501];
int lamp[501][3];
bool st[501];
int unsolved[501];
bool vis[501];
bool check_lamp(int k)
{
for (int j = 1; j <= swit[k][0]; ++j) if (swit[k][j] > 0 && st[swit[k][j]] || swit[k][j] < 0 && !st[-swit[k][j]]) return true;
return false;
}
int main(int argc, char *argv[])
{
int n, m;
char str[5];
while (cin >> n >> m)
{
for (int i = 1; i <= m; ++i) lamp[i][0] = 0;
for (int i = 1; i <= n; ++i)
{
cin >> swit[i][0];
for (int j = 1; j <= swit[i][0]; ++j)
{
scanf("%d %s", swit[i] + j, str);
lamp[swit[i][j]][++lamp[swit[i][j]][0]] = i;
if (str[1] == 'F') swit[i][j] = -swit[i][j];
}
}
int step = 0;
unsolved[0] = 0;
MEMSET(vis, 0);
for (int i = 1; i <= n; ++i)
{
if (!check_lamp(i))
{
unsolved[++unsolved[0]] = i;
vis[i] = true;
}
}
while (step < 100000 && unsolved[0])
{
++step;
int u = unsolved[unsolved[0]--];
vis[u] = false;
if (check_lamp(u)) continue;
int idx = rand() % swit[u][0] + 1;
int s = abs(swit[u][idx]);
st[s] = !st[s];
for (int i = 1; i <= lamp[s][0]; ++i)
{
if (lamp[s][i] == u) continue;
if (!check_lamp(lamp[s][i]) && !vis[lamp[s][i]])
{
vis[lamp[s][i]] = true;
unsolved[++unsolved[0]] = lamp[s][i];
}
}
}
if (unsolved[0]) puts("-1");
else
{
for (int i = 1; i < m; ++i) printf("%s ", st[i] ? "ON" : "OFF");
puts(st[m] ? "ON" : "OFF");
}
}
return 0;
}