%%% http://wenku.baidu.com/link?url=Y55O7adUuZ1biB4m_9wdrJhts9KqqLwnMlI25xgxcpUok9Gv5CrRK9kV9Rxci2Eq8DDcfGi6I9gzVQURJv0kywjihAtkkE61mAss8ZO8qOW
对于每盏灯,有方程组xor{X[adj[i]]} = 1。
解异或方程组即可。
可以尝试把ts >= ans 改成ts > ans会发生啥。
(其实就是16ms -> 48ms的区别而已)
然而压位并没有优化时间,毕竟N=35。。。
#include <cstdio>
#define FOR(i,l,r) for (int i=l;i<=r;++i)
int read() {
int s = 0, f = 1; char ch = getchar();
for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') f = -1;
for (; '0' <= ch && ch <= '9'; ch = getchar()) s = s * 10 + ch - '0';
return s * f;
}
long long a[38], tmp;
int n, ans = 2147483647;
bool sta[38];
void dfs(int x, int ts) {
if (ts >= ans) return;
if (!x) { ans = ts; return; }
if (a[x] & (1ll << x)) {
sta[x] = a[x] & 1ll << n + 1;
FOR(k,x+1,n) sta[x] ^= (a[x] & 1ll << k ? 1 : 0) & sta[k];
dfs(x - 1, ts + sta[x]);
} else {
sta[x] = 1; dfs(x - 1, ts + sta[x]);
sta[x] = 0; dfs(x - 1, ts + sta[x]);
}
}
int main() {
int m, x, y;
n = read(); m = read();
FOR(i,1,n) a[i] |= 1ll << i, a[i] |= 1ll << n + 1;
FOR(i,1,m) {
x = read(); y = read();
a[x] |= 1ll << y; a[y] |= 1ll << x;
}
FOR(i,1,n) {
FOR(j,i,n) if ((a[j] & 1ll << i) > (a[i] & 1ll << i)) tmp = a[i], a[i] = a[j], a[j] = tmp;
FOR(j,1,n) if (i != j && (a[j] & 1ll << i)) a[j] ^= a[i] & (1ll << 63) - (1ll << i);
}
dfs(n, 0);
printf("%d\n", ans);
return 0;
}
1770: [Usaco2009 Nov]lights 燈
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 606 Solved: 290
Description
貝希和她的閨密們在她們的牛棚中玩遊戲。但是天不從人願,突然,牛棚的電源跳閘了,所有的燈都被關閉了。貝希是一個很膽小的女生,在伸手不見拇指的無盡的黑暗中,她感到驚恐,痛苦與絕望。她希望您能夠幫幫她,把所有的燈都給重新開起來!她才能繼續快樂地跟她的閨密們繼續玩遊戲! 牛棚中一共有N(1 <= N <= 35)盞燈,編號為1到N。這些燈被置於一個非常複雜的網絡之中。有M(1 <= M <= 595)條很神奇的無向邊,每條邊連接兩盞燈。 每盞燈上面都帶有一個開關。當按下某一盞燈的開關的時候,這盞燈本身,還有所有有邊連向這盞燈的燈的狀態都會被改變。狀態改變指的是:當一盞燈是開著的時候,這盞燈被關掉;當一盞燈是關著的時候,這盞燈被打開。 問最少要按下多少個開關,才能把所有的燈都給重新打開。 數據保證至少有一種按開關的方案,使得所有的燈都被重新打開。
Input
*第一行:兩個空格隔開的整數:N和M。
*第二到第M+1行:每一行有兩個由空格隔開的整數,表示兩盞燈被一條無向邊連接在一起。 沒有一條邊會出現兩次。
Output
第一行:一個單獨的整數,表示要把所有的燈都打開時,最少需要按下的開關的數目。
Sample Input
1 2
1 3
4 2
3 4
2 5
5 3
輸入細節:
一共有五盞燈。燈1、燈4和燈5都連接著燈2和燈3。
Sample Output
輸出細節:
按下在燈1、燈4和燈5上面的開關。