bzoj 1770: [Usaco2009 Nov]lights 燈 (高斯消元)

本文介紹了一道Usaco2009Nov的競賽題目lights的解決方案,該問題要求通過最少的操作使所有燈亮起。文章詳細闡述了使用高斯消元法進行優化求解的過程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1770: [Usaco2009 Nov]lights 燈

Time Limit: 10 Sec   Memory Limit: 64 MB
Submit: 820   Solved: 387
[ Submit][ Status][ Discuss]

Description

貝希和她的閨密們在她們的牛棚中玩遊戲。但是天不從人願,突然,牛棚的電源跳閘了,所有的燈都被關閉了。貝希是一個很膽小的女生,在伸手不見拇指的無盡的黑暗中,她感到驚恐,痛苦與絕望。她希望您能夠幫幫她,把所有的燈都給重新開起來!她才能繼續快樂地跟她的閨密們繼續玩遊戲! 牛棚中一共有N(1 <= N <= 35)盞燈,編號為1到N。這些燈被置於一個非常複雜的網絡之中。有M(1 <= M <= 595)條很神奇的無向邊,每條邊連接兩盞燈。 每盞燈上面都帶有一個開關。當按下某一盞燈的開關的時候,這盞燈本身,還有所有有邊連向這盞燈的燈的狀態都會被改變。狀態改變指的是:當一盞燈是開著的時候,這盞燈被關掉;當一盞燈是關著的時候,這盞燈被打開。 問最少要按下多少個開關,才能把所有的燈都給重新打開。 數據保證至少有一種按開關的方案,使得所有的燈都被重新打開。

Input

*第一行:兩個空格隔開的整數:N和M。

*第二到第M+1行:每一行有兩個由空格隔開的整數,表示兩盞燈被一條無向邊連接在一起。 沒有一條邊會出現兩次。

Output

第一行:一個單獨的整數,表示要把所有的燈都打開時,最少需要按下的開關的數目。

Sample Input

5 6
1 2
1 3
4 2
3 4
2 5
5 3

輸入細節:

一共有五盞燈。燈1、燈4和燈5都連接著燈2和燈3。

Sample Output

3

輸出細節:

按下在燈1、燈4和燈5上面的開關。

HINT

Source

[ Submit][ Status][ Discuss]

HOME   Back


题解:高斯消元

需要对自由元进行dfs,在dfs的时候需要进行最优化剪枝,否则会TLE。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 100
using namespace std;
int n,m,point[N],a[N][N],nxt[N],v[N],b[N],ans[N];
int cnt,q[N],mn,bt[N],size,now;
void dfs(int i)
{
	if (!i) {
		mn=min(mn,now);
		return;
	}
	if (now>=mn) return;
	if (a[i][i]) {
	   for (int j=i+1;j<=n;j++)
	    if (a[i][j]) b[i]^=ans[j];
	   ans[i]=b[i];
	   if (b[i]) now++;
	   dfs(i-1);
	   if (b[i]) now--;
	   for (int j=i+1;j<=n;j++)
	    if (a[i][j]) b[i]^=ans[j];	
	}
	else {
		ans[i]=1; now++; dfs(i-1);
		ans[i]=0; now--; dfs(i-1);
	}
}
void guass()
{
	for (int i=1;i<=n;i++) {
		int j=i;
		for (int t=i+1;t<=n;t++)
		 if (a[t][i]) {
		   j=t;
		   break;
	    }
		if (a[j][i]==0) continue;
		if (j!=i) {
			swap(b[j],b[i]);
			for (int t=1;t<=n;t++) swap(a[i][t],a[j][t]);
		}
		for (int t=1;t<=n;t++)
		 if (a[t][i]&&t!=i) {
		 	b[t]^=b[i];
		 	for (int j=1;j<=n;j++) 
		 	 a[t][j]^=a[i][j];
		 }
	}
	//for (int i=1;i<=n;i++,cout<<endl)
	// for (int j=1;j<=n;j++) cout<<a[i][j]<<" ";
	//for (int i=1;i<=n;i++) cout<<b[i]<<" ";
	//cout<<endl;
	dfs(n);
	//for (int i=1;i<=n;i++) cout<<ans[i]<<" ";
	//cout<<endl;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d%d",&n,&m); mn=n;
	for (int i=1;i<=n;i++) b[i]=1,a[i][i]=1;
	for (int i=1;i<=m;i++) {
	 int x,y; scanf("%d%d",&x,&y);
	 a[x][y]=a[y][x]=1;
	}
	//for (int i=1;i<=n;i++,cout<<endl)
	// for (int j=1;j<=n;j++) cout<<a[i][j]<<" ";
	guass();
	printf("%d\n",mn);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值