【例题】【高斯消元(异或)】

1、
NKOJ1985 中央暖气
时间限制 : 10000 MS 空间限制 : 65536 KB

问题描述
冬天来了,但URAL大学的暖气系统还没启动。这个暖气系统包括许多阀门,只有所有阀门都打开了才能供应暖气。大学里有一些技术员,他们每人负责一个或多个阀门,有可能存在一个阀门由多个人负责的情况。当一个技术员接到启动暖气系统的指示后他就会把所有自己负责的阀门都转动一次。这意味着如果一个阀门原来是开的,他就把它关掉;如果原来是关的,就把它打开。任何一个技术员的工作都不可能让其他人代劳。
你的任务是决定哪些技术员应接到指示,从而使整个大学的暖气系统启动。注意学校里一共有N个技术员和N个阀门(1 <= N <= 250)。

输入格式
输入的第一行是一个数字N(1 <= N <= 250)。下面的N行是每一个技术员负责的阀门的列表。其中第i行包含第i个技术员所负责的阀门的编号。每一个阀门列表都以-1结束。

输出格式
输出满足条件的升序的技术员编号序列。如果有好几个序列满足要求,就输出最短的一个。如果无法找到方法把整个学校的暖气系统启动,就输出”No solution” .

样例输入
4
1 2 -1
2 3 4 -1
2 -1
4 -1

样例输出
1 2 3

来源 ural 1042

思路:
“任何一个技术员的工作都不可能让其他人代劳”,是有无解或有唯一解的意思…
所以遇到变元就可以判无解了

#include
#include
using namespace std;
const int need=257;

int n;
int m[need][need+1];
int ans[need];

int gauss(int xx,int yy)
{
    int x,y,i,j,mx;
    for(x=y=1;x<=xx&&yfor(i=x;i<=xx;i++) if(m[i][y]==1) {mx=i;break;}
        if(mx!=x)
         for(i=y;i<=yy;i++) swap(m[mx][i],m[x][i]);
        if(m[x][y]==0) {return -1;}//必定无解或唯一解 
        for(i=x+1;i<=xx;i++)
         if(m[i][y])
         for(j=y;j<=yy;j++) m[i][j]^=m[x][j];
    }
    //for(i=x;i<=xx;i++) if(m[i][yy]) return -1;
    int temp;
    for(i=xx;i>=1;i--)
    {
        temp=m[i][yy];
        for(j=i+1;j//m[i][i]必定为1 
    }
}

int main()
{
    cin>>n;
    for(int i=1,a;i<=n;i++)
    {
        cin>>a;
        for(;a!=-1;cin>>a) m[a][i]=1;
    }
    for(int i=1;i<=n;i++) m[i][n+1]=1;
    if(gauss(n,n+1)==-1) puts("No solution");
    else for(int i=1;i<=n;i++) if(ans[i]) cout<" ";
}

2、
NKOJ1986 开关问题
时间限制 : 10000 MS 空间限制 : 65536 KB

问题描述
有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)

输入格式
输入第一行有一个数K,表示以下有K组测试数据。
每组测试数据的格式如下:
第一行 一个数N(0 < N < 29)
第二行 N个0或者1的数,表示开始时N个开关状态。
第三行 N个0或者1的数,表示操作结束后N个开关的状态。
接下来 每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。每组数据以 0 0 结束。

输出格式
如果有可行方法,输出总数,否则输出“Oh,it’s impossible~!!” 不包括引号

样例输入
2
3
0 0 0
1 1 1
1 2
1 3
2 1
2 3
3 1
3 2
0 0
3
0 0 0
1 0 1
1 2
2 1
0 0

样例输出
4
Oh,it’s impossible~!!

提示
第一组数据的说明:
一共以下四种方法:
操作开关1
操作开关2
操作开关3
操作开关1、2、3 (不记顺序)

来源 poj 1830

思路:统计变元个数即可,不必求出具体解

#include
#include
#include
using namespace std;
const int need=45;

int n;
int s[need];
int m[need][need+1];
int ans[need];

int gauss(int xx,int yy)
{
    int x,y,i,j,mx;
    for(x=y=1;x<=xx&&yfor(i=x;i<=xx;i++) if(m[i][y]==1) {mx=i;break;}
        if(mx!=x)
         for(i=y;i<=yy;i++) swap(m[mx][i],m[x][i]);
        if(m[x][y]==0) {x--;continue;}
        for(i=x+1;i<=xx;i++)
         if(m[i][y])
         for(j=y;j<=yy;j++) m[i][j]^=m[x][j];
    }
    for(i=x;i<=xx;i++) if(m[i][yy]) return -1;
    return xx-x+1;
}

int main()
{
    int t;cin>>t;
    int aa;
    int ta,tb;
while(t--)
{
    cin>>n;
    for(int i=1,j;i<=n;i++)
     for(j=1;j<=n+1;j++)
      m[i][j]=0;
    for(int i=1;i<=n;i++) cin>>s[i];
    for(int i=1;i<=n;i++) cin>>m[i][n+1],m[i][n+1]^=s[i];//开关状态与原来同还是反 
    for(int i=1;i<=n;i++) m[i][i]=1;
    while(true)
    {
        cin>>ta>>tb;
        if(ta==0&&tb==0) break;
        m[ta][tb]=1;
    }
    aa=gauss(n,n+1);
    if(aa==-1) puts("Oh,it's impossible~!!");
    else cout<<(1<//自由变元只有两种取值 
}
}

NKOJ1987 手机游戏
时间限制 : 10000 MS 空间限制 : 65536 KB

问题描述
有一个有趣的手机游戏,有n*n个正方形的小按钮,有的按钮是黄色,有的按钮是白色。玩家的任务是通过点击按钮,让所有按钮都变成黄色,点按钮的次数越少,得分越高。
但是按钮有个奇怪的特点,当你点击了坐标为(x,y)的按钮后,坐标为(x,y),(x+1,y),(x-1,y),(x,y-1),(x,y+1)的五个按钮会同时改变自身的颜色,是白色的变成黄色,黄色的变成白色。完成游戏最少需要点击多少次按钮呢?请找出答案。

【例题】【高斯消元(异或)】_第1张图片

输入格式
第一行,一个整数n,表示有n*n个按钮。(1<=n<=40)
接下来是一个由小写字母’y’和字符’w’构成的n*n的矩阵,描述了游戏开始时的情景。’y’表示该按钮式黄色,’w’表示该按钮式白色。

输出格式
如果能够完成游戏,输出一个整数,表示所需最小点击次数。
如果无法完成游戏,输出”inf”

样例输入
样例输入1:
2
yw
ww

样例输入2:
5
wwwww
wwwww
wwwww
wwwww
wwwww

样例输出
样例输出1:
1

样例输出2:
15

来源 改编自poj 1681

思路:变元不能直接取为0,需深搜找最优解…
//深搜复杂度O(2^k),k为变元个数….

#include
#include
using namespace std;
const int need=42;

int n,nn;
int m[need*need][need*need+1];
int ans[need*need];
int num[need][need];
char ma[need][need];
int kx[]={0,0,1,-1},ky[]={1,-1,0,0};
int free_x[need],tot; 
bool isfree[need];

int gauss(int xx,int yy)
{
    int x,y,i,j,mx;
    for(x=y=1;x<=xx&&yfor(i=x;i<=xx;i++) if(m[i][y]==1) {mx=i;break;}
        if(mx!=x)
         for(i=y;i<=yy;i++) swap(m[mx][i],m[x][i]);
        if(m[x][y]==0) {isfree[y]=true;free_x[++tot]=y;x--;continue;}
        for(i=x+1;i<=xx;i++)
         if(m[i][y])
         for(j=y;j<=yy;j++) m[i][j]^=m[x][j];
    }
    for(i=x;i<=xx;i++) if(m[i][yy]) return -1;
    int temp,ans2=0;
    if(tot!=0)
    {
        ans2=1e9;
        int tans;
        int nn=(1<1;
        for(int s=0,i,j,k;s<=nn;s++)
        {
            tans=0;
            for(j=0;jif(s&(1<1]]=1,tans++;
             else ans[free_x[j+1]]=0; 
            for(i=xx;i>=1;i--)
            {
                if(isfree[i]) continue;
                temp=m[i][yy];
                for(j=i+1;jif(ans[i]) tans++;
            }
            ans2=min(ans2,tans);
        }
        return ans2;
    }
    for(i=xx;i>=1;i--)
    {
        temp=m[i][yy];
        for(j=i+1;j//m[i][i]必定为1 
        if(ans[i]) ans2++;
    }
    return ans2;
}

int main()
{
    cin>>n;
    nn=n*n;
    for(int i=1,j,tot=0;i<=n;i++)
     for(j=1;j<=n;j++)
      num[i][j]=++tot;
    for(int i=1;i<=nn;i++) m[i][nn+1]=1;

    for(int i=1;i<=n;i++) scanf("%s",&ma[i][1]);

    for(int i=1,j;i<=n;i++)
     for(j=1;j<=n;j++)
      if(ma[i][j]=='y') m[num[i][j]][nn+1]=0;

    for(int i=1,j,k,xx,yy;i<=n;i++)
     for(j=1;j<=n;j++)
     {
        m[num[i][j]][num[i][j]]=1;
        for(k=0;k<4;k++)
        {
            xx=kx[k]+i,yy=ky[k]+j;
            if(xx>=1&&xx<=n&&yy>=1&yy<=n) m[num[xx][yy]][num[i][j]]=1;
        }
     }
    int tt=gauss(nn,nn+1);
    if(tt==-1) cout<<"inf";
    else cout<

你可能感兴趣的:(【例题】【高斯消元(异或)】)