1.POJ-1222 EXTENDED LIGHTS OUT
題意
開關問題,給一個矩陣,每次翻轉,上下左右都會一起翻轉,問你翻轉哪些位置,可以把燈全部關上。
思路
異或運算下的高斯消元
典型的開關問題,和 POJ 1830 開關問題 是一樣的屬於 XOR 類型的消元。
/***************************
*author:ccf
*source:poj-1222- EXTENDED LIGHTS OUT
*topic:高斯消元
****************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 40;
const int inf = 50;
int cas,n,icas = 1;
int a[N][N];//增廣矩陣
int equ,var;//equ:行 var: 列
int free_i[N],free_cnt;
int x[N],mp[N];//x[N]自由元數組
//void debug() {
// for(int i = 0 ; i < equ; i++) {
// for(int j = 0; j <= var; j++)
// printf("%d ",a[i][j]);
// printf("\n");
// }
//
//}
bool Gauss() {
int k,col,max_i;//k是列 col是行 max_i是最大行
//轉化爲階梯陣
for(k = col = 0; k < equ && col < var; k++,col++) {
//找到最大行
max_i = k;
for(int i = k + 1; i < equ; i++) {
if(abs(a[i][col]) > abs(a[max_i][col])) max_i = i;
}
if(a[max_i][col] == 0) {
free_i[col] = 1;
x[free_cnt++] = col;
k--;
continue;
}
if(k != max_i) {
for(int j = col; j <= var; j++) {
swap(a[max_i][j],a[k][j]);
}
}
//開始化簡階梯陣
for(int i = 0; i < equ; i++) {
if(a[i][col] && i != k)
for(int j = col; j <= var; j++)
a[i][j] ^= a[k][j];
}
}
//debug();
for(int i = k; i < equ; i++)
if(a[i][var]) return false;
return true;
}
int main() {
//freopen("date.in","r",stdin);
equ = var = 30;
scanf("%d",&cas);
while(cas--) {
int cnt = 0;
memset(a,0,sizeof(a));
memset(free_i,0,sizeof(free_i));
memset(x,0,sizeof(x));
memset(mp,0,sizeof(mp));
for(int i = 0; i < equ; i++) {
scanf("%d",&mp[i]);
a[i][var] = mp[i];
}
//方格化成增廣矩陣
for(int i = 0 ; i < equ; i++) {
a[i][i] = 1;
if(i < 24) a[i][i+6] = 1;
if(i > 5) a[i][i-6] = 1;
if(i % 6 != 0) a[i][i-1] = 1;
if(i % 6 != 5) a[i][i+1] = 1;
}
if(!Gauss()) printf("no solution!\n");
else {
printf("PUZZLE #%d\n",icas++);
for(int i = 1; i <= 30; i++) {
printf("%d",a[i-1][var]);
if(i % 6 == 0) printf("\n");
else printf(" ");
}
}
}
return 0;
}
2.POJ-2947-Widget Factory
題意
有n種飾品,每種的加工時間爲3~9天,現在知道m條記錄,每條記錄形如:開始時間是周幾,終止時間是周幾,加工出來哪些飾品,各多少件。但是不知道持續了多少周。
求每種飾品的加工時間。
需要判斷無解和多解。
思路
高斯消元解同餘方程
在原本高斯約旦消元的基礎上,加上取模的運算,最後每一行得到一個
形式的式子,使用擴展歐幾里得解同餘方程即可,也有直接模擬取餘操作的方法。
/***********************
*author:ccf
*source:poj-2974 Widget Factory
*topic:高斯消元解同餘方程
************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 600;
int cas,n,m,icas;
char start[5],fired[5];
int a[N][N],ans[N];//行列式 解集
int equ,var,free_cnt = 0;
void debug() {
for(int i = 1; i <= equ; i++) {
for(int j = 1; j <= var + 1; j++) {
printf("%d ",a[i][j]);
}
printf("\n");
}
printf("\n");
}
int sti(char *s) {
if(strcmp(s, "MON") == 0) return 1;
else if(strcmp(s, "TUE") == 0) return 2;
else if(strcmp(s, "WED") == 0) return 3;
else if(strcmp(s, "THU") == 0) return 4;
else if(strcmp(s, "FRI") == 0) return 5;
else if(strcmp(s, "SAT") == 0) return 6;
else if(strcmp(s, "SUN") == 0) return 7;
}
int gcd(int a, int b) {
return b == 0 ? a : gcd(b,a%b);
}
int lcm(int a,int b) {
return a / gcd(a,b) * b;
}
int Gauss() {
int free_i[N],free_x[N],free_cnt = 0;
int k,col,max_i;
for(k = col = 1; k <= equ && col <= var; k++,col++) {
max_i = k;
//找到最大值
for(int i = k + 1; i <= equ; i++) {
if(abs(a[i][col]) > abs(a[max_i][col])) max_i = i;
}
//交換最大值
if(k != max_i) {
for(int j = k; j <= var + 1; j++) {
swap(a[max_i][j],a[k][j]);
}
}
//確定自由元
if(a[k][col] == 0) {
free_x[++free_cnt] = col;
k--;
continue;
}
//化簡行列式
for(int i = 1; i <= equ; i++) {
if(a[i][col] != 0 && i != k) {
int LCM = lcm(abs(a[i][col]),abs(a[k][col]));
int tmp1 = LCM/abs(a[i][col]),tmp2 = LCM/abs(a[k][col]);
if(a[i][col] * a[k][col] < 0) tmp2 = -tmp2;
for(int j= 1; j <= var + 1; j++) {
a[i][j] = ( (a[i][j] * tmp1 - a[k][j] * tmp2) % 7 + 7) % 7;
}
}//debug();
}
}
//無解
for(int i = k ; i <= equ; i++)
if(a[i][var+1]) return -1;
//存在自由元 多個解
if(k <= var) return var - k + 1;
//只有一個解
for(int i = var; i > 0; i--) {
int tmp = a[i][var + 1];
for(int j = i + 1; j <= var; j++) {
if(a[i][j] != 0) tmp -= a[i][j] * ans[j];
tmp = (tmp % 7 + 7) % 7;
}
while(tmp % a[i][i] != 0) tmp += 7;
ans[i] = (tmp / a[i][i] )% 7;
}
return 0;
}
int main() {
freopen("data.in","r",stdin);
while(scanf("%d %d",&n,&m) && n + m != 0) {
memset(a,0,sizeof a);
memset(ans,0,sizeof ans);
equ = m;
var = n;
//化爲階梯陣
for(int i = 1,k; i <= m; i++) {
scanf("%d %s %s",&k,start,fired);
//printf("%d %s %s\n",k,start,fired);
a[i][n + 1] =((sti(fired) - sti(start) + 1)%7 + 7) % 7;
for(int j = 1,tmp; j <= k; j++) {
scanf("%d",&tmp);
a[i][tmp]++;
a[i][tmp] %= 7;
}
}
//debug();
int res = Gauss();
if(res == -1) printf("Inconsistent data.\n");
else if(res == 0) {
for(int i = 1; i <= n; i++) {
if(ans[i] < 3) ans[i] += 7;
printf("%d",ans[i]);
if(i == n) printf("\n");
else printf(" ");
}
} else {
printf("Multiple solutions.\n");
}
}
return 0;
}
3.POJ-1681-Painter’s Problem
題意
異或運算下的高斯消元
給方格染色,原本是白的,可以染成黃色,每次染一個格子,上下左右的方格就都會翻轉,問全染成黃色的最小染色次數是多少?
思路
仍屬於開關問題,XOR 操作下的高斯消元。
/*******************
*author:ccf
*source:poj 1681 - Painter's Problem
*topic:高斯消元
*******************/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 251;
int cas,n;
int a[N][N], mp[N];
int equ,var,ans = 0;
void debug(){
for(int i= 1 ; i <= n*n; i++){
for(int j = 1; j <= n*n + 1; j++)
printf("%d ",a[i][j]);
printf("\n");
}
printf("\n");
}
bool Gauss(){
int free_x[N],free_cnt = 0;
int k,col,max_i;
for(k = col = 1; k <= equ && col <= var; k++,col++){
//找最大值
max_i = k;
for(int i = k + 1; i <= equ; i++){
if(a[max_i][col] < a[i][col]) max_i = i;
}
if(k != max_i){
for(int j = col; j <= var + 1; j++)
swap(a[max_i][j],a[k][j]);
}
//獲取自由元
if(a[k][col] == 0){
free_x[++free_cnt] = col;
k--;
continue;
}
//化簡階梯陣
for(int i = 1; i <= equ; i++){
if(i != k && a[i][col]){
for(int j = col; j <= var + 1; j++)
a[i][j] ^= a[k][j];
}
}
//debug();
}
//存在 0 = 1 的情況,無解
for(int i = k; i <=equ ; i++)
if(a[i][var + 1]) return false;
//debug();
for(int i = 1; i <= equ; i++)
if(a[i][var + 1] ) ans++;
return true;
}
int main(){
//freopen("date.in","r",stdin);
scanf("%d",&cas);
while(cas--){
int cnt = 0;
char c;
ans = 0;
memset(a,0,sizeof a);
scanf("%d",&n);
equ = var = n*n;
for(int i = 1; i <= n; i++){
getchar();
for(int j = 1; j <= n; j++){
scanf("%c",&c);
if(c == 'y') mp[++cnt] = 1;
else mp[++cnt] = 0;
}
}
//方格轉化爲增廣矩陣
for(int i = 1; i <= cnt; i++){
a[i][i] = 1;
a[i][var + 1] = 1 ^ mp[i];
if(i > n) a[i][i-n] = 1;
if(i < n * (n-1) + 1) a[i][i+n] = 1;
if(i % n != 0) a[i][i+1] = 1;
if(i % n != 1) a[i][i-1] = 1;
}
//debug();
if(!Gauss()) printf("inf\n");
else{
printf("%d\n",ans);
}
}
return 0;
}
/*
1
3
yyy
yyy
yyy
*/
4.POJ-2065-SETI
題意
思路
高斯消元解同餘方程組
和POJ-2947-Widget Factory 是一樣的,高斯消元時帶上取模運算,最後使用擴展歐幾里得算法進行同餘方程的求解。
/***********************
*author:ccf
*source:POJ-2065-SETI
*topic:高斯消元解同餘方程
************************/
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;
const int N = 107;
int equ,var,cas,p;
int a[N][N],ans[N];
char info[N];
void debug() {
for(int i = 1; i <=equ; i++) {
for(int j = 1; j <= var + 1; j++)
printf("%d ",a[i][j]);
printf("\n");
}
printf("\n");
}
int gcd(int a,int b) {
return b == 0 ? a : gcd(b,a%b);
}
int lcm(int a,int b) {
return a / gcd(a,b) * b;
}
int exgcd(int a,int b,int &x,int &y){
if(b == 0){
x = 1;y = 0;
return a;
}
int t = exgcd(b,a%b,x,y);
int x0 = x, y0= y;
x = y0;
y = x0 - a / b * y0;
return t;
}
int qpow(int a, int b,int mod) {
int res = 1;
while(b) {
if(b & 1) {
res = res * a % mod;
}
a = a * a % mod;
b >>= 1;
}
return res;
}
int sti(char *s) {
int len = strlen(s);
for(int i = 0; i < len; i++) {
if(s[i] == '*') a[i + 1][len + 1] = 0;
else a[i + 1][len + 1] = (s[i] - 'a' + 1) % p;
}
return len;
}
int Gauss() {
int free_x[N],free_i[N],free_cnt = 0;
int k,col,max_i;
for(k = col = 1; k <= equ && col <= var; k++,col++) {
max_i = k;
//找出最大值並交換
for(int i = k + 1; i <= equ; i++)
if(abs(a[i][col]) > abs(a[max_i][col])) max_i = i;
if(k != max_i) {
for(int j = col; j <= var + 1; j++)
swap(a[k][j],a[max_i][j]);
}
if(a[k][col] == 0) {
free_x[++free_cnt] = col;
k--;
continue;
}
//化簡階梯陣
for(int i = 1; i <= equ; i++) {
if(i != k && a[i][col]) {
int LCM = lcm(abs(a[i][col]),abs(a[k][col]));
int tmp1 = LCM / abs(a[i][col]),tmp2 = LCM / abs(a[k][col]);
if(a[i][col] * a[k][col] < 0) tmp2 = -tmp2;
for(int j = 1; j <= var + 1; j++) {
a[i][j] = ((a[i][j] * tmp1 - a[k][j] * tmp2) % p + p) % p;
}
}
//debug();
}
}
for(int i = k; i <= equ; i++)
if(a[i][var + 1])return -1;
return 0;
}
void solve(){
int x,y;
for(int i = 1; i <= var; i++){
int A = a[i][i], B = p, C = a[i][var + 1];
int g = exgcd(A,B,x,y);
A /= g;
B /= g;
C /= g;
int x0 = x * C;
x0 = (x0 % B + B ) % B;
ans[i] = x0;
}
}
int main() {
//freopen("data.in","r",stdin);
scanf("%d",&cas);
while(cas--) {
memset(a,0,sizeof a);
memset(ans,0,sizeof ans);
scanf("%d %s",&p,info);
int len = sti(info);
equ = var = len;
for(int i = 1; i <= len; i++) {
for(int j = 1; j <= len; j++) {
a[i][j] = qpow(i,j-1,p);
}
}
//debug();
if(Gauss() != -1){
solve();
for(int i = 1; i <= len; i++)
printf("%d ",ans[i]);
}else {
printf("No Smoking!");
}
puts("");
}
return 0;
}
5.POJ-1753 - Flip Game
題意
給一個棋盤,可以黑色白色棋子相互翻轉,每次翻轉一個棋子,其上下左右的棋子也會翻轉。變成全白或全黑的時候結束,問結束的最小步數是多少?
思路
高斯消元 + 枚舉不定元
開關問題,但有一點不同的是:當存在不定變元的時候,我們要枚舉每個不定元的狀態來求出最小值, 個不定元一共有種狀態,用數組記錄不定元,使用二進制來枚舉可加快速度。
/*******************
*author:ccf
*source:poj 1753 - Flip Game
*topic:高斯消元
*******************/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define ll long long
using namespace std;
const int N = 20;
int cas,n = 4;
int a[N][N], mp[N];
int equ,var;
//void debug(){
// for(int i= 1 ; i <= n*n; i++){
// for(int j = 1; j <= n*n + 1; j++)
// printf("%d ",a[i][j]);
// printf("\n");
// }
// printf("\n");
//}
int Gauss(){
int ans = N;
int free_x[N],free_cnt = 0;
int free_num[N],free_i[N];
int k,col,max_i;
memset(free_num,0,sizeof(free_num));
memset(free_x,0,sizeof(free_x));
memset(free_i,0,sizeof(free_i));
for(k = col = 1; k <= equ && col <= var; k++,col++){
//找最大值
max_i = k;
for(int i = k + 1; i <= equ; i++){
if(a[max_i][col] < a[i][col]) max_i = i;
}
if(k != max_i){
for(int j = col; j <= var + 1; j++)
swap(a[max_i][j],a[k][j]);
}
//獲取自由元
if(a[k][col] == 0){
free_i[col] = 1;
free_x[++free_cnt] = col;
k--;
continue;
}
//化簡階梯陣
for(int i = 1; i <= equ; i++){
if(i != k && a[i][col]){
for(int j = col; j <= var + 1; j++)
a[i][j] ^= a[k][j];
}
}
//debug();
}
//存在 0 = 1 的情況,無解
for(int i = k; i <=equ ; i++)
if(a[i][var + 1]) return N;
//debug();
int limit = 1 << free_cnt;
//for(int i = 1; i <= free_cnt; i++) printf("%d ",free_x[i]);
for(int i = 0; i < limit; i++){
int num = 0,m = i;
memset(free_num,0,sizeof free_num);
//使用二進制來開始枚舉自由元
for(int j = 1; j <= free_cnt; j++){
if(m & 1){
free_num[free_x[j]] = 1;
num++;
}
m /= 2;
}
//根據不定元確定主元
for(int j = k - 1; j >= 1; j--){
if(!free_i[j]){
int tmp = a[j][var+1];
for(int l = j + 1; l <= var; l++)
if(a[j][l]) tmp ^= free_num[l];
free_num[j] = tmp;
if(tmp) num++;
}
}
//printf("num = %d\n",num);
ans = min(ans,num);
}
return ans;
}
int main(){
int cnt = 0;
char c;
memset(a,0,sizeof a);
equ = var = 16;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
scanf("%c",&c);
if(c == 'b') mp[++cnt] = 1;
else mp[++cnt] = 0;
}
getchar();
}
//方格轉化爲增廣矩陣
for(int i = 1; i <= cnt; i++){
a[i][i] = 1;
a[i][var + 1] = 0 ^ mp[i];
if(i > n) a[i][i-n] = 1;
if(i < n * (n-1) + 1) a[i][i+n] = 1;
if(i % n != 0) a[i][i+1] = 1;
if(i % n != 1) a[i][i-1] = 1;
}
//debug();
int tmp = Gauss();
memset(a,0,sizeof a);
for(int i = 1; i <= cnt; i++){
a[i][i] = 1;
a[i][var + 1] = 1 ^ mp[i];
if(i > n) a[i][i-n] = 1;
if(i < n * (n-1) + 1) a[i][i+n] = 1;
if(i % n != 0) a[i][i+1] = 1;
if(i % n != 1) a[i][i-1] = 1;
}
tmp = min(tmp,Gauss());
if(tmp == N) printf("Impossible\n");
else{
printf("%d\n",tmp);
}
return 0;
}
/*
bwwb
bbwb
bwwb
bwww
*/