HDU 2828 Lamp

题目链接

题目大意:有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;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值