bzoj 1770: [Usaco2009 Nov]lights 燈

1770: [Usaco2009 Nov]lights 燈

Time Limit: 10 Sec  Memory Limit: 64 MB
Submit: 976  Solved: 463
[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上面的開關。
  
  这道题讲真看不出来是爆搜,唯一的提示就是N很小,但又大于状压范围,可能需要爆搜。
  这道题正解是异或高斯消元+DFS。很明显一个灯要么不按,要么就按一次,所以我们很容易想到高斯消元,由于这道题要求都亮所以异或和之后的结果都为1。通过异或高斯消元我们就可以知道那些点对于某个点是否真正有影响,那些点自己是否开对自己是否有影响,然后我们就可以对于每个自己开不开都对自己没影响的点(自由元)进行枚举,看他们开ans小还是关ans小,而对于每一个我们已知对自己有影响的就去看他是否被我们打开,由于高斯消元之后是一个倒三角形,我们只用从他之后那位开始找就好了,如果前几个开开他是关上的我们就把他打开,否则就把他关上,最终输出答案即可。
  
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 #define N 50
10 using namespace std;
11 int n,m,ans=0x7ffffff;
12 int a[N][N],ope[N];
13 void dfs(int sum,int wz)
14 {
15     if(sum>=ans)return;
16     if(wz==0) 
17     {
18         ans=sum;return;
19     }
20     if(a[wz][wz])
21     {
22         int l=a[wz][n+1];
23         for(int i=wz+1;i<=n;i++) if(a[wz][i]) l^=ope[i];
24         if(l) 
25         {
26             ope[wz]=1;
27             sum++;
28         }
29         dfs(sum,wz-1);
30         if(l)
31         {
32             ope[wz]=0;
33             sum--;
34         }
35     } 
36     else
37     {
38         ope[wz]=1;sum++;dfs(sum,wz-1);
39         ope[wz]=0;sum--;dfs(sum,wz-1);
40     }
41 }
42 void gx()
43 {
44     for(int i=1;i<=n;i++)
45     {
46         int bj=i;
47         while(bj<=n&&!a[bj][i])bj++;
48         if(bj>n)continue;
49         if(bj!=i)
50         {
51             for(int j=1;j<=n+1;j++)
52             {
53                 swap(a[i][j],a[bj][j]);
54             }
55         }
56         for(int j=1;j<=n;j++)
57         {
58             if(i==j||!a[j][i])continue;
59             for(int k=1;k<=n+1;k++)
60                 a[j][k]^=a[i][k];
61         }
62     }
63 }
64 int main()
65 {
66     scanf("%d%d",&n,&m);
67     for(int i=1;i<=m;i++)
68     {
69         int x,y;
70         scanf("%d%d",&x,&y);
71         a[x][y]=a[y][x]=1;
72     }
73     for(int i=1;i<=n;i++)
74         a[i][i]=1,a[i][n+1]=1;
75     gx();
76     dfs(0,n);
77     printf("%d\n",ans);
78     return 0;
79 }
View Code

   对了,说句题外话,BZOJ的工作效率挺快的,中午才把送过去的错的ID改正,下午就是权限了。

转载于:https://www.cnblogs.com/liutianrui/p/7536679.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值