任意门:http://poj.org/problem?id=3185
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 7676 | Accepted: 3036 |
Description
Their snouts, though, are so wide that they flip not only one bowl but also the bowls on either side of that bowl (a total of three or -- in the case of either end bowl -- two bowls).
Given the initial state of the bowls (1=undrinkable, 0=drinkable -- it even looks like a bowl), what is the minimum number of bowl flips necessary to turn all the bowls right-side-up?
Input
Output
Sample Input
0 0 1 1 1 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0
Sample Output
3
Hint
Flip bowls 4, 9, and 11 to make them all drinkable:
0 0 1 1 1 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 [initial state]
0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0 [after flipping bowl 4]
0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 [after flipping bowl 9]
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [after flipping bowl 11]
Source
题意概括:
N个开关,打开一个开关相邻的开关状态会取反,给一个初始的所有开关状态,要求求出最小的改变开关的次数使得所有开关的状态为关闭;
解题思路:
构造增广矩阵类似于根据开关的关系构造有向图的邻接矩阵;
构造增广矩阵,高斯消元,枚举*元(二进制枚举状态),寻找最小值;
AC code:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;
const int MAXN = ;
int a[MAXN][MAXN]; //增广矩阵
int freeX[MAXN]; //*元
int x[MAXN]; //解集
int equ, var;
int free_num;
int N; int Gauss()
{
int maxRow, col, k;
free_num = ;
for(k = , col = ; k < equ && col < var; k++, col++){
maxRow = k;
for(int i = k+; i < equ; i++){
if(abs(a[i][col]) > abs(a[maxRow][col])){
maxRow = i;
}
} if(a[maxRow][col] == ){
k--;
freeX[free_num++] = col;
continue;
}
if(maxRow != k){
for(int j = col; j < var+; j++){
swap(a[k][j], a[maxRow][j]);
}
} for(int i = k+; i < equ; i++){
if(a[i][col] != ){
for(int j = col; j < var+; j++)
a[i][j] ^= a[k][j]; }
}
} for(int i = k; i < equ; i++) //无解
if(a[i][col] != ) return -; if(k < var) return var-k; //多解返回*元个数 for(int i = var-; i >= ; i--){ //唯一解,回代
x[i] = a[i][var];
for(int j = i+; j < var; j++){
x[i] ^= (a[i][j] && x[j]);
}
}
return ;
} void solve()
{
int t = Gauss();
if(t == -){ //无解的情况,其实题目保证有解
printf("inf\n");
return;
}
else if(t == ){ //唯一解
int ans = ;
for(int i = ; i < N; i++){
ans += x[i];
}
printf("%d\n", ans);
return;
}
else{ //多解,枚举*元
int ans = INF;
int tot = (<<t);
for(int i = ; i < tot; i++){
int cnt = ;
for(int j = ; j < t; j++){
if(i&(<<j)){
x[freeX[j]] = ;
cnt++;
}
else x[freeX[j]] = ;
} for(int j = var-t-; j >= ; j--){
int index;
for(index = j; index < var; index++)
if(a[j][index])
break;
x[index] = a[j][var]; for(int s = index+; s < var; s++)
if(a[j][s])
x[index] ^= x[s];
cnt += x[index];
}
ans = min(ans, cnt);
}
printf("%d\n", ans);
}
return;
} int main()
{
N = ;
equ = ;
var = ;
memset(a, , sizeof(a));
memset(x, , sizeof(x));
for(int i = ; i < N; i++){
a[i][i] = ;
if(i > ) a[i-][i] = ;
if(i < N-) a[i+][i] = ;
}
for(int i = ; i < N; i++){
scanf("%d", &a[i][N]);
}
solve();
return ;
}