题意 开关问题,给一个矩阵,每次翻转,上下左右都会一起翻转,问你翻转哪些位置,可以把灯全部关上。
思路
异或运算下的高斯消元
典型的开关问题,和 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; }题意 有n种饰品,每种的加工时间为3~9天,现在知道m条记录,每条记录形如:开始时间是周几,终止时间是周几,加工出来哪些饰品,各多少件。但是不知道持续了多少周。 求每种饰品的加工时间。
需要判断无解和多解。 思路
高斯消元解同余方程
在原本高斯约旦消元的基础上,加上取模的运算,最后每一行得到一个 A x ≡ C ( m o d p ) Ax\equiv C(mod p) Ax≡C(modp)形式的式子,使用扩展欧几里得解同余方程即可,也有直接模拟取余操作的方法。
/*********************** *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; }题意
异或运算下的高斯消元
给方格染色,原本是白的,可以染成黄色,每次染一个格子,上下左右的方格就都会翻转,问全染成黄色的最小染色次数是多少?
思路
仍属于开关问题,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 */题意 思路
高斯消元解同余方程组
和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; }题意
给一个棋盘,可以黑色白色棋子相互翻转,每次翻转一个棋子,其上下左右的棋子也会翻转。变成全白或全黑的时候结束,问结束的最小步数是多少?
思路
高斯消元 + 枚举不定元
开关问题,但有一点不同的是:当存在不定变元的时候,我们要枚举每个不定元的状态来求出最小值, c n t cnt cnt 个不定元一共有 2 c n t 2^{cnt} 2cnt种状态,用数组记录不定元,使用二进制来枚举可加快速度。
/******************* *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 */