poj 1681 Painter's Problem

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;
}
View Code

 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;
}
View Code

 

posted @ 2016-02-04 15:39  hxer  阅读(312)  评论(0编辑  收藏  举报