poj 1681 Painter's Problem
题意:给一个n*n(1 <= n <= 15)具有初始颜色(颜色只有yellow&white两种,即01矩阵)的square染色,每次对一个方格染成黄色时,同时会把周围的方格也染成黄色。(这和1222的开关一样的关联关系)问最后可以将square全部染成黄色的最小染色方格数?
思路:
1.直接预处理出增广矩阵,和1222不同的是里面有最优解的条件,贪心的思想是把自由变元看成是没染色的,但是其他非自由变元(除去自由维度之外的变量)是可以通过自由变元的取值来确定的(在poj 1753中WA了很久,有是一个坑), 但是这道题确实可以不用枚举过。。
2.本题的var = 15*15,变量的个数很大,直接枚举自由变元会不会导致枚举TLE?或者二进制枚举爆数位?这就转化为了方程自由变元的最大个数?如果只考虑高斯消元的全部变量,虽然可以从之间的关联来看会极大的减小自由度,却依旧难证明。但是如果从枚举第一行,就可以递推出全部结果就知道其实是一个一维的自由度,不超过15~~
code1:直接将自由元看成0...(也能A,不够严谨)0ms..
#include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) int dir[2][4] = {{0,1,0,-1},{1,0,-1,0}}; int a[230][230]; int equ,var; int x[230],free_var[230]; void debug() { puts("********"); int i,j; rep0(i,0,equ){ rep1(j,0,var) cout<<a[i][j]<<" "; cout<<endl; }puts("********"); } int Guass() { int i,j,k,row,col,cnt = 0; for(row = 0,col = 0;row < equ && col < var;row++,col++){ int mx = row; rep0(j,row+1,equ) if(abs(a[j][col]) > abs(a[mx][col])) mx = j; if(a[mx][col] == 0){ row--; // 行不变;不能通过这里记录自由变元的个数,只能记录没用的col free_var[col] = ++cnt;//记录自由变元的标号; continue; } if(mx != row) rep1(k,col,var) swap(a[row][k],a[mx][k]); rep0(j,row+1,equ){ if(a[j][col]){ rep1(k,col,var) a[j][k] ^= a[row][k]; } } } //debug(); int use_equ = row; //有用的方程数即能确定的变元的个数 rep0(i,use_equ,equ) if(a[i][var] != 0) return -1; //无解 //if(use_equ < var) return var - use_equ;//row表示有用的方程数方程,但是要在判断出有解的前提下才能说有多组解; rep_1(i,use_equ-1,0){ x[i] = a[i][var]; rep0(j,i+1,use_equ) x[i] ^= (a[i][j] && x[j]); //第j个灯会影响到第i盏灯,同时第j盏灯也会亮 } } void init(int n) { int i,j,k; rep0(i,0,n) rep0(j,0,n){ int id = i*n+j; a[id][id] = 1; rep0(k,0,4){ int nx = i + dir[0][k] ,ny = j + dir[1][k]; if(nx < 0 || nx >= n || ny < 0 || ny >= n) continue; a[nx*n+ny][id] = 1; } } } int main() { int T,n,i; cin>>T; while(T--){ MS0(x);MS0(a);MS0(free_var); scanf("%d",&n); equ = var = n*n ; rep0(i,0,var){ char c = getchar(); if(c == 'w') a[i][var] = 1; else if(c == 'y') a[i][var] = 0; else i--; } init(n); int ret = Guass(); if(ret == -1) puts("inf"); else{ int ans = 0; rep0(i,0,var)if(free_var[i] == 0) ans += x[i]; printf("%d\n",ans); } } return 0; }
code2:枚举自由变元:(16ms)
注意:含有自由变元的式子也是等式。。就是指开始枚举出了自由变量的个数,在后面会不会重复计算。
#include<iostream> #include<cstdio> #include<cstring> #include<string.h> #include<algorithm> #include<map> #include<queue> #include<vector> #include<cmath> #include<stdlib.h> #include<time.h> using namespace std; #define rep0(i,l,r) for(int i = (l);i < (r);i++) #define rep1(i,l,r) for(int i = (l);i <= (r);i++) #define rep_0(i,r,l) for(int i = (r);i > (l);i--) #define rep_1(i,r,l) for(int i = (r);i >= (l);i--) #define MS0(a) memset(a,0,sizeof(a)) #define MS1(a) memset(a,-1,sizeof(a)) #define inf 0x3f3f3f3f int dir[2][4] = {{0,1,0,-1},{1,0,-1,0}}; int a[230][230]; int equ,var; int x[230],free_var[230]; void debug() { puts("********"); int i,j; rep0(i,0,equ){ rep1(j,0,var) cout<<a[i][j]<<" "; cout<<endl; }puts("********"); } int Guass() { int i,j,k,row,col,cnt = 0; for(row = 0,col = 0;row < equ && col < var;row++,col++){ int mx = row; rep0(j,row+1,equ) if(abs(a[j][col]) > abs(a[mx][col])) mx = j; if(a[mx][col] == 0){ row--; // 行不变;不能通过这里记录自由变元的个数,只能记录没用的col free_var[cnt++] = col;//记录自由变元的标号; continue; } if(mx != row) rep1(k,col,var) swap(a[row][k],a[mx][k]); rep0(j,row+1,equ){ if(a[j][col]){ rep1(k,col,var) a[j][k] ^= a[row][k]; } } } //debug(); //row即为有用的方程数即能确定的变元的个数 rep0(i,row,equ) if(a[i][var] != 0) return -1; //无解 //枚举自由变元,row表示有用的方程数方程,但是要在判断出有解的前提下才能说有多组解; //if(row < var) return var - row; //当不需要枚举时,直接返回自由变元的个数 int ans = inf,tot = 1 <<(var - row); rep0(i,0,tot){ int cnt = 0,tmp = i; rep0(j,0,var - row){ x[free_var[j]] = (tmp&1); if(x[free_var[j]]) cnt++;//** tmp >>= 1; } rep_1(i,row-1,0){ //自由变元不会相互影响,所以可以不分 x[i] = a[i][var];//现在赋为a[i][var],若为自由变元之后还是会等于0,不会重复计算; rep0(j,i+1,equ){ x[i] ^= (a[i][j] && x[j]); //第j个灯会影响到第i盏灯,同时第j盏灯也会亮 } if(x[i]) cnt++; } ans = min(ans,cnt); } return ans; } void init(int n) { int i,j,k; rep0(i,0,n) rep0(j,0,n){ int id = i*n+j; a[id][id] = 1; rep0(k,0,4){ int nx = i + dir[0][k] ,ny = j + dir[1][k]; if(nx < 0 || nx >= n || ny < 0 || ny >= n) continue; a[nx*n+ny][id] = 1; } } } int main() { int T,n,i; cin>>T; while(T--){ MS0(x);MS0(a);MS0(free_var); scanf("%d",&n); equ = var = n*n ; rep0(i,0,var){ char c = getchar(); if(c == 'w') a[i][var] = 1; else if(c == 'y') a[i][var] = 0; else i--; } init(n); int ret = Guass(); if(ret == -1) puts("inf"); else printf("%d\n",ret); } return 0; }