您的位置:首页 > 大数据 > 人工智能

POJ 1681 Painter's Problem (高斯消元 枚举自由变元求最小的步数)

2014-08-18 17:00 435 查看
题目链接

题意:

一个n*n 的木板 ,每个格子 都 可以 染成 白色和黄色,( 一旦我们对也个格子染色 ,他的上下左右 都将改变颜色);

给定一个初始状态 , 求将 所有的 格子 染成黄色 最少需要染几次? 若 不能 染成 输出 inf。

分析:

和1222差不多,唯一的区别是这个题还要求 最短的步数,其实只需要枚举一下最后的x[][]是否为1,即是否需要按下,

由于只有无解或者解唯一,因为按的顺序是没有影响的,所以只要是有解一定唯一,而且最短的情况是每个格子只按一次,

因为按两次以后就变为原来的状态了。

这个是最严谨的这个题的AC代码,因为我做1753的时候用原来的方法错了,看了Kuangbin的博客,知道原来的方法不对,貌似只有一个解和无解的时候才会对。

但是这个是对的:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define LL __int64
const int maxn = 300+10;
const int INF = 1<<28;
using namespace std;
int equ, var, fn;
int a[maxn][maxn], x[maxn];
int free_x[maxn];
int gcd(int a, int b)
{
return b==0?a:gcd(b, a%b);
}
int lcm(int a, int b)
{
return a*b/gcd(a, b);
}
int Gauss()
{
int x_mo;
x_mo = 2;
int i, j, k, max_r, col;
int ta, tb, LCM, fx_num = 0;
col = 0;

for(k = 0; k<equ && col<var; k++, col++)
{
max_r = k;
for(i = k+1; i < equ; i++)
if(abs(a[i][col])>abs(a[max_r][col]))
max_r = i;

if(max_r != k)
for(j = k; j < var+1; j++)
swap(a[k][j], a[max_r][j]);

if(a[k][col]==0)
{
free_x[fx_num++] = col; //求自由变元所在的列
k--;
continue;
}
for(i = k+1; i < equ; i++)
{
if(a[i][col] != 0)
{
LCM = lcm(abs(a[i][col]), abs(a[k][col]));
ta = LCM/abs(a[i][col]);
tb= LCM/abs(a[k][col]);
if(a[i][col]*a[k][col] < 0) tb = -tb;

for(j = col; j < var+1; j++)
a[i][j] = ((a[i][j]*ta - a[k][j]*tb)%x_mo+x_mo)%x_mo;
}
}
}
for(i = k; i < equ; i++)
if(a[i][col] != 0)
return -1;

//以下为模2情况下的,枚举变元的方法。
int stat=1<<(var-k);//自由变元有 var-k 个
int res=INF;
for(i=0;i<stat;i++)//枚举所有变元
{
int cnt=0;
int index=i;
for(j=0;j<var-k;j++)
{
x[free_x[j]]=(index&1);
if(x[free_x[j]]) cnt++;
index>>=1;
}
for(j=k-1;j>=0;j--)
{
int tmp=a[j][var];
for(int l=j+1;l<var;l++)
if(a[j][l]) tmp^=x[l];
x[j]=tmp;
if(x[j])cnt++;
}
if(cnt<res)res=cnt;
}
return res;
}

int main()
{
int t, i, j, n, tmp;
char s[maxn];
scanf("%d", &t);
while(t--)
{
memset(a, 0, sizeof(a));
memset(x, 0, sizeof(x));
scanf("%d", &n);
equ = n*n;
var = n*n;
for(i = 0; i < n; i++)
{
getchar();
scanf("%s", s);
for(j = 0; j < n; j++)
{
if(s[j]=='y') a[i*n+j][n*n] = 0; //y时为0
else a[i*n+j][n*n] = 1;
}
}
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
tmp = i*n+j;
a[tmp][tmp] = 1;
if(j<=n-2)
a[tmp+1][tmp] = 1;
if(j>=1)
a[tmp-1][tmp] = 1;
if(tmp+n<n*n)
a[tmp+n][tmp] = 1;
if(tmp-n>=0)
a[tmp-n][tmp] = 1;
}
fn = Gauss();
if(fn==-1)
printf("inf\n");
else
printf("%d\n", fn);
}
return 0;
}


这个AC代码是我按照模板改的,但是在多解的情况下不对:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define LL __int64
const int maxn = 300+10;
using namespace std;
int equ, var, fn;
int a[maxn][maxn], x[maxn];
int gcd(int a, int b)
{
return b==0?a:gcd(b, a%b);
}
int lcm(int a, int b)
{
return a*b/gcd(a, b);
}
int Gauss()
{
int x_mo;
x_mo = 2;
int i, j, k, max_r, col;
int ta, tb, LCM, tmp;
col = 0;

for(k = 0; k<equ && col<var; k++, col++)
{
max_r = k;
for(i = k+1; i < equ; i++)
if(abs(a[i][col])>abs(a[max_r][col]))
max_r = i;

if(max_r != k)
for(j = k; j < var+1; j++)
swap(a[k][j], a[max_r][j]);

if(a[k][col]==0)
{
k--;
continue;
}
for(i = k+1; i < equ; i++)
{
if(a[i][col] != 0)
{
LCM = lcm(abs(a[i][col]), abs(a[k][col]));
ta = LCM/abs(a[i][col]);
tb= LCM/abs(a[k][col]);
if(a[i][col]*a[k][col] < 0) tb = -tb;

for(j = col; j < var+1; j++)
a[i][j] = ((a[i][j]*ta - a[k][j]*tb)%x_mo+x_mo)%x_mo;
}
}
}
for(i = k; i < equ; i++)
if(a[i][col] != 0)
return -1;

for(i = var-1; i >= 0; i--)
{
tmp = a[i][var];
for(j = i+1; j < var; j++)
if(a[i][j] != 0)
tmp = ((tmp-a[i][j]*x[j])%x_mo+x_mo)%x_mo;
if(a[i][i]==0)  //注意这a[i][i]可能为0, 我改为这样就对了。
x[i] = 0;
else
{
while(tmp%a[i][i]!=0) tmp += x_mo;
x[i] = (tmp/a[i][i])%x_mo;
}
}
return 0;
}

int main()
{
int t, i, j, n, ans, tmp;
char s[maxn];
scanf("%d", &t);
while(t--)
{
memset(a, 0, sizeof(a));
memset(x, 0, sizeof(x));
scanf("%d", &n);
equ = n*n;
var = n*n;
for(i = 0; i < n; i++)
{
getchar();
scanf("%s", s);
for(j = 0; j < n; j++)
{
if(s[j]=='y') a[i*n+j][n*n] = 0; //y时为0
else a[i*n+j][n*n] = 1;
}
}
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
tmp = i*n+j;
a[tmp][tmp] = 1;
if(j<=n-2)
a[tmp+1][tmp] = 1;
if(j>=1)
a[tmp-1][tmp] = 1;
if(tmp+n<n*n)
a[tmp+n][tmp] = 1;
if(tmp-n>=0)
a[tmp-n][tmp] = 1;
}
fn = Gauss();
if(fn==-1)
printf("inf\n");
else
{
ans = 0;
for(i = 0; i < n*n; i++)
if(x[i]==1)  //枚举解为1,就是需要将原来的翻转的。
ans ++;
printf("%d\n", ans);
}
}
return 0;
}


这个AC代码的模板用的是别人的对二取模的模板,用的是 ^异或,不会出现除0的情况。在多解的情况下也不对。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define LL __int64
const int maxn = 300+10;
using namespace std;
int equ, var, fn;
int a[maxn][maxn], x[maxn];
int gcd(int a, int b)
{
return b==0?a:gcd(b, a%b);
}
int lcm(int a, int b)
{
return a*b/gcd(a, b);
}
int  Gauss()
{
int i,j,k;
int max_r;
int col;
int temp;

int free_x_num;
int free_index;

col=0;
for(k=0;k<equ&&col<var;k++,col++)
{
max_r=k;
for(i=k+1;i<equ;i++)
{
if(abs(a[i][col])>abs(a[max_r][col]))max_r=i;
}
if(max_r!=k)
{
for(j=col;j<var+1;j++)swap(a[k][j],a[max_r][j]);
}
if(a[k][col]==0)
{
k--;
continue;
}
for(i=k+1;i<equ;i++)
{
if(a[i][col]!=0)
{
for(j=col;j<var+1;j++)
a[i][j]^=a[k][j];
}
}
}
for(i=k;i<equ;i++)
{
if(a[i][col]!=0)return -1;
}
for(i=var-1;i>=0;i--)
{
x[i]=a[i][var];
for(j=i+1;j<var;j++)
x[i]^=(a[i][j]&&x[j]);
}
return 0;
}

int main()
{
int t, i, j, n, ans, tmp;
char s[maxn];
scanf("%d", &t);
while(t--)
{
memset(a, 0, sizeof(a));
memset(x, 0, sizeof(x));
scanf("%d", &n);
equ = n*n;
var = n*n;
for(i = 0; i < n; i++)
{
getchar();
scanf("%s", s);
for(j = 0; j < n; j++)
{
if(s[j]=='y') a[i*n+j][n*n] = 0;
else a[i*n+j][n*n] = 1;
}
}
for(i = 0; i < n; i++)
for(j = 0; j < n; j++)
{
tmp = i*n+j;
a[tmp][tmp] = 1;
if(j<=n-2)
a[tmp+1][tmp] = 1;
if(j>=1)
a[tmp-1][tmp] = 1;
if(tmp+n<n*n)
a[tmp+n][tmp] = 1;
if(tmp-n>=0)
a[tmp-n][tmp] = 1;
}
fn = Gauss();
if(fn==-1)
printf("inf\n");
else
{
ans = 0;
for(i = 0; i < n*n; i++)
if(x[i]==1)
ans ++;
printf("%d\n", ans);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: