林大陈宇老师的原创高斯

最后1 行,有x4=b4;
第3 行: x4+x3=b3,已经求出x4=b4 了,带入第3 行,可求出x3;同理,把x4 x3 带入
第2 行,还可求出x2;把x4 x3 x2 带入到第1 行,可求出x1;
这就是回带。
回带总结:从最后1 行,逐一往回带,从最后1 行代回到第1 行。
最关键的时候到了:当无穷多解时,最后几行都是0;没法回带;而某些题目在无穷多
解时还要你求最小或最优解,没办法,就得枚举最后行为0 的那几个解;
如:
1 2 4 3 3
0 1 3 2 3
0 0 1 0 2
0 0 0 0 0
可见最后的x4 没法解,如果题中给定x4 的范围,那就枚举x4,然后回带;枚举1 次x4
就回带1 次得到一组(x1 x2 x3 x4 );然后根据题意找最优的,具体题目具体分析。
说得够详细的了,下面拿出有效的高消模板:
-----------------------------------再次复习过程--------------------------------------------------------------
确定系数矩阵和增广矩阵
消元
If 如果有系数矩阵=0,增广矩阵不为0,则无解;
{
例: 0 0 0 0 2
有这样的行就没解,0*x1+0*x2+0*x3+0*x4=2;没这样的x
}
If (var-k>0) //有多个解//var 变量数,k 是主对角线上连续1 的个数
{
枚举最后几行(全为0) 的那几个解;枚举或dfs 都行
回带可以求出很多组解;
}
If (var-k==0) //唯一解
{
直接回带
}
-----------------------------复习完成----------------------------------------------------------------------

int a[maxn][maxn+1],x[maxn];//a 是系数矩阵和增广矩阵,x 是最后存放的解
// a[][maxn]中存放的是方程右面的值(bi)
int equ,var;//equ 是系数阵的行数,var 是系数矩阵的列数(变量的个数)
int free_num,ans=100000000;
int abs1(int num) //取绝对值
{
if (num>=0) return num;
else
return -1*num;
}
void Debug(void) //调试输出,看消元后的矩阵值,提交时,就不用了
{
int i, j;
for (i = 0; i < equ; i++)
{
for (j = 0; j < var + 1; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
inline int gcd(int a, int b) //最大公约数
{
int t;
while (b != 0)
{
t = b;
b = a % b;
a = t;
}
return a;
}

inline int lcm(int a, int b) //最小公倍数
{
return a * b / gcd(a, b);
}
void swap(int &a,int &b){int temp=a;a=b;b=temp;} //交换2 个数
int Gauss()
{
int k,col = 0; //当前处理的列
for(k = 0;k < equ && col < var;++k,++col)
{
int max_r = k;
for(int i = k+1;i < equ; ++i)
if(a[i][col] > a[max_r][col])
max_r = i;
if(max_r != k)
{
for(int i = k;i < var + 1; ++i)
swap(a[k][i],a[max_r][i]);
}
if(a[k][col] == 0)
{
k--;
continue;
}
for(int i = k+1;i < equ; ++i)
{
if(a[i][col] != 0)
{
int LCM = lcm(a[i][col],a[k][col]);
int ta = LCM/a[i][col], tb = LCM/a[k][col];
if(a[i][col]*a[k][col] < 0)
tb = -tb;
for(int j = col;j < var + 1; ++j)
a[i][j] = ( (a[i][j]*ta)%2 - (a[k][j]*tb)%2 + 2 ) % 2; //a[i][j]只有
0 和1 两种状态
}
}
}

//上述代码是消元的过程,行消元完成
//解下来2 行,判断是否无解
//注意K 的值,k 代表系数矩阵值都为0 的那些行的第1 行
for(int i = k;i < equ; ++i)
if(a[i][col] != 0) return -1; // 无解返回 -1
//Debug();
//唯一解或者无穷解,k<=var
//var-k==0 唯一解;var-k>0 无穷多解,自由解的个数=var-k
//能执行到这,说明肯定有解了,无非是1 个和无穷解的问题。
//下面这几行很重要,保证秩内每行主元非0,且按对角线顺序排列,就是检查列
for(int i = 0; i <equ; ++i)//每一行主元素化为非零
if(!a[i][i])
{
int j;
for(j = i+1;j<var;++j)
if(a[i][j])
break;
if(j == var)
break;
for(int k = 0;k < equ; ++k)
swap(a[k][i],a[k][j]);
}
// ----处理保证对角线主元非0 且顺序,检查列完成
// free_num=k;
if (var-k>0) {
//无穷多解,先枚举解,然后用下面的回带代码进行回带;
//这里省略了下面的回带的代码;不管唯一解和无穷解都可以回带,只不过无穷解
//回带时,默认为最后几个自由变元=0 而已。
}
if (var-k==0)//唯一解时
{
//下面是回带求解代码,当无穷多解时,最后几行为0 的解默认为0;
for(int i = k-1;i >= 0; --i) //从消完元矩阵的主对角线非0 的最后1 行,开始往
//回带
{
int tmp = a[i][var] % 2;

for(int j = i+1;j < var; ++j) //x[i]取决于x[i+1]--x[var]啊,所以后面的解对前面的解
有影响。
if(a[i][j] != 0)
tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
//if (a[i][i]==0) x[i]=tmp; //最后的空行时,即无穷解得
//else
x[i] = (tmp/a[i][i]) % 2; //上面的正常解
}
//回带结束了
}
}
--------------------------对上面模板的说明---------------------------------------------------------------
因为我们要解决的问题,基本上都是解都是出于0-1 这2 个数,所以要对2 取余;
如果想对普通的正数解方程,那自己把对2 取余删掉就行了;
下面这几道题要各个搞定才行:
POJ 1222 EXTENDED LIGHTS OUT
http://acm.pku.edu.cn/JudgeOnline/problem?id=1222
POJ 1681 Painter's Problem
http://acm.pku.edu.cn/JudgeOnline/problem?id=1681
POJ 1753 Flip Game
http://acm.pku.edu.cn/JudgeOnline/problem?id=1753
POJ 1830 开关问题
http://acm.pku.edu.cn/JudgeOnline/problem?id=1830
POJ 3185 The Water Bowls
http://acm.pku.edu.cn/JudgeOnline/problem?id=3185
开关窗户,开关灯问题,很典型的求解线性方程组的问题。方程数和变量数均为
行数*列数,直接套模板求解即可。但是,当出现无穷解时,需要枚举解的情况,
因为无法判断哪种解是题目要求最优的。
POJ 2947 Widget Factory poj 2065
http://acm.pku.edu.cn/JudgeOnline/problem?id=2947
求解同余方程组问题。与一般求解线性方程组的问题类似,只要在求解过程中加
入取余即可。
注意:当方程组唯一解时,求解过程中要保证解在[3, 9]之间。
分下类: 1222 有唯一解 太简单

1753 3185 2965 高斯+枚举,我要详细讲(黑板上)
2947 2065 同余方程
先说1222 这题:
题目大意:给你一个5*6 的矩阵,矩阵里每一个单元都有一个灯和一个
开关,如果按下此开关,那么开关所在位置的那个灯和开关前后左右的
灯的状态都会改变(即由亮到不亮或由不亮到亮)。给你一个初始的灯的
状态,问怎样控制每一个开关使得所有的灯最后全部熄灭(此题保证有
唯一解)。
解题思路:高斯消元。很显然每个灯最多只需要按1 下(因为按两
下和没有按是一个效果)。我们可以定义30 和未知数x0、
x1.......x29 代表每一个位置的开关是否被按。那么对于每一个灯
的状态可以列一个方程,假设位置(i,j)处的开关为x(i*6+j),那么
我们就可以列出方程:
x(i*6+j)+x((i-1)*6+j)+x((i+1)*6+j)+x(i*6+j-1)+x(i*6+j+1) =
bo(mod 2)
(括号里的数字为x 的下标,这里假设这些下标都是符合要求的,即
都在矩形内,如果不在则可以去掉,当这个灯初始时是开着的,那
么bo 为1,否则为0)
这样可以列出30 个方程,然后用高斯消元解这个方程组即可。
我的补充:题目给定后,我立刻知道了该题,呵呵,有唯一解;
怎么判断出的呢? 5 行6 列,已经固定了;每个灯的方程也固定了(系数矩阵);只有
开始时灯的状态会变,开始时灯的状态只能决定增广矩阵的最后1 列;因为系数矩阵式固定
的,而系数矩阵能决定该方程是否有唯一解和多个解;编好代码后,自己 deBUG 一下,也
就是把a 这个矩阵输出一下,自己look 一下,发现没有都是0 的行,秩=5;
所以模板里只要算一下 (var-k==0)是,回带就行了;
#include <iostream>
#include <cstring>
#include <cmath>
#include <stdio.h>
using namespace std;

const int maxn = 30;
int equ, var; // 有equ 个方程,var 个变元。增广阵行数为equ, 分别为0 到equ - 1,列数为
var + 1,分别为0 到var.
int a[maxn][maxn+1];
int x[maxn]; // 解集.
bool free_x[maxn+1]; // 判断是否是不确定的变元.
int free_num;
int abs1(int num)
{
if (num>=0) return num;
else
return -1*num;
}
void Debug(void)
{
int i, j;
for (i = 0; i < equ; i++)
{
for (j = 0; j < var + 1; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
inline int gcd(int a, int b)
{
int t;
while (b != 0)
{
t = b;
b = a % b;

a = t;
}
return a;
}
inline int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
void swap(int &a,int &b){int temp=a;a=b;b=temp;}
int Gauss_new()
{
int k,col = 0; //当前处理的列
for(k = 0;k < equ && col < var;++k,++col)
{
int max_r = k;
for(int i = k+1;i < equ; ++i)
if(a[i][col] > a[max_r][col])
max_r = i;
if(max_r != k)
{
for(int i = k;i < var + 1; ++i)
swap(a[k][i],a[max_r][i]);
}
if(a[k][col] == 0)
{
k--;
continue;
}
for(int i = k+1;i < equ; ++i)
{
if(a[i][col] != 0)
{
int LCM = lcm(a[i][col],a[k][col]);
int ta = LCM/a[i][col], tb = LCM/a[k][col];
if(a[i][col]*a[k][col] < 0)
tb = -tb;
for(int j = col;j < var + 1; ++j)

a[i][j] = ( (a[i][j]*ta)%2 - (a[k][j]*tb)%2 + 2 ) % 2; //a[i][j]只有0 和
1 两种状态
}
}
}
for(int i = k;i < equ; ++i)
if(a[i][col] != 0) return -1; // 无解返回 -1
//唯一解或者无穷解,k<=var,这里直接回带了,知道有唯一解啊
for(int i = k-1;i >= 0; --i)
{
int tmp = a[i][var] % 2;
for(int j = i+1;j < var; ++j)
if(a[i][j] != 0)
tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
x[i] = (tmp/a[i][i]) % 2;
}
return 0;
}
int main(void)
{
// freopen("Input.txt", "r", stdin);
int i, j,t,t1;
cin>>t;
t1=t;
equ=30;
var=30;
while (t--)
{
memset(a, 0, sizeof(a));
memset(x, 0, sizeof(x));
//memset(free_x, 1, sizeof(free_x)); // 一开始全是不确定的变元.
//下面要根据位置计算a[i][j];

for (i = 0; i < 5; i++)
{
for (j = 0; j < 6; j++)
{
/* for(int k=0;k<4;k++)
{
int ni=i+di[k];
int nj=j+dj[k];
if(inlim(ni,nj))
{
a[i*6+j][ni*6+nj]=1;
}
}
*/
if (i-1>=0) a[i*6+j][(i-1)*6+j]=1; //计算上面的位置
if (i+1<=4) a[i*6+j][(i+1)*6+j]=1;//计算下面的位置
if (j-1>=0) a[i*6+j][i*6+j-1]=1;//计算左面的位置
if (j+1<=5) a[i*6+j][i*6+j+1]=1; //计算右面的位置
a[i*6+j][i*6+j]=1;//别忘了计算自己
cin>>a[i*6+j][30];
//scanf("%d", &a[i][j]);
}
}
// Debug
//free_num = Gauss();
free_num=Gauss_new();
if (free_num == -1) printf("无解!\n");
else if (free_num >= 0)
{
int na_num=0;
printf("PUZZLE #%d\n",t1-t);
for (i = 0; i < var; i++)
{
na_num++;
if (na_num%6==0) {printf("%d\n",x[i]);}
else

printf("%d ",x[i]);
}
}
// printf("\n");
}
return 0;
}
------------------------------------1222-----------over--------------------------------------------------------------------
重点说下1753 这题:本题用guass 还是比较牛的,速度很快啊
题意:
有4*4 的正方形,每个格子要么是黑色,要么是白色,当把一个格子的颜色改变
(黑->白或者白->黑)时,其周围上下左右(如果存在的话)的格子的颜色也被反转,
问至少反转几个格子可以使4*4 的正方形变为纯白或者纯黑?
本题求都变白或变黑的最小格子数。
分析:求都变白的最小格子数;再求变黑的最小格子数,输出2 者最小值即可;
这里说下本类问题的bi 的赋值,bi 就是曾广矩阵的最后1 列;
其实就是开始的矩阵(0-1),和最终矩阵的(0-1)做异或;相同的格子为0,不同的格子值
为1;这类问题都一样的;
本体4*4 的矩阵固定,方法固定,只是初始状态不固定,还是先把代码先敲进去,用样例输
入,用 debug()把消元后的a 的结果输出到屏幕上看看,看看有几个解是未知的,共16 个

方程:
1100100000000000
0111001000000000
0010110000000000
0001110100000000
0000110101000000
0000011110000000
0000001101010000
0000000111100000
0000000010111000
0000000001111010
0000000000110100
0000000000010011
0000000000000000
0000000000000000
0000000000000000
0000000000000000
最后4 行都是0,则最后4 个解变量是无穷解;
X15 x14 x13 x12 只能取0 或1,枚举 2^4 共16 中情况,枚举1 次,回带1 次,算出1
组解,记录解变量和最小的。
0 000 0001 0010 0011 ----------1111 共16 个,说得太详细了,枚举可以循环
For (i=1----16)
{
0 0 0 0 // 每次根据i 值,产生0 0 0 0 还是 00 10 ,就是x15 x14 x13 x12 的值
回带
得到解
和是否最小
}
这是16 种情况,要是更多,还是用dfs()吧,也简单的要命!
--------------------------------下位直接枚举的代码---超短--------------------------------
• for(i=0,min=17;i<16;++i){ //枚举有解的16 种情况
• for(j=15;j>11;--j)r[j]=(1<<(j-12))&i?1:0; //最后4 变量是自由变量
仔细体会上面这2 句,如何产生 0 0 0 0 ----1111 的
----------------------------------高消+枚举-------poj1753-----------------------------------------------------------

#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
const int maxn=16;
int a[maxn][maxn+1],x[maxn],b[maxn][maxn+1];
int equ,var;
bool free_x[maxn+1]; // 判断是否是不确定的变元.
int free_num,ans=100000000;
int abs1(int num)
{
if (num>=0) return num;
else
return -1*num;
}
void Debug(void)
{
int i, j;
for (i = 0; i < equ; i++)
{
for (j = 0; j < var + 1; j++)
{
cout << a[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
inline int gcd(int a, int b)
{
int t;
while (b != 0)
{
t = b;
b = a % b;

a = t;
}
return a;
}
inline int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
int dfs(int p) //枚举自由解,只能取0-1,枚举完就回带,找到最小的
{
if (p<=free_num-1) //深入到了主对角线元素非0 的行了
{ //下面就是回带的代码啊
for(int i = free_num-1;i >= 0; --i)
{
int tmp = a[i][var] % 2;
for(int j = i+1;j < var; ++j) //x[i]取决于x[i+1]--x[var]啊,所以后面的解对前面的解有影
响。
if(a[i][j] != 0)
tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
x[i] = (tmp/a[i][i]) % 2; //上面的正常解
} //回带完成了
//计算解元素为1 的个数;
int sum=0;
for(int i=0;i<var;i++) sum+=x[i];
if (ans>sum) ans=sum;
return 0;
}
x[p]=0;dfs(p-1);
x[p]=1;dfs(p-1);
}
void swap(int &a,int &b){int temp=a;a=b;b=temp;}
int Gauss_1()
{
int k,col = 0; //当前处理的列
for(k = 0;k < equ && col < var;++k,++col)

{
int max_r = k;
for(int i = k+1;i < equ; ++i)
if(a[i][col] > a[max_r][col])
max_r = i;
if(max_r != k)
{
for(int i = k;i < var + 1; ++i)
swap(a[k][i],a[max_r][i]);
}
if(a[k][col] == 0)
{
k--;
continue;
}
for(int i = k+1;i < equ; ++i)
{
if(a[i][col] != 0)
{
int LCM = lcm(a[i][col],a[k][col]);
int ta = LCM/a[i][col], tb = LCM/a[k][col];
if(a[i][col]*a[k][col] < 0)
tb = -tb;
for(int j = col;j < var + 1; ++j)
a[i][j] = ( (a[i][j]*ta)%2 - (a[k][j]*tb)%2 + 2 ) % 2; //a[i][j]只有0 和
1 两种状态
}
}
}
for(int i = k;i < equ; ++i)
if(a[i][col] != 0) return -1; // 无解返回 -1
//上述代码是消元的过程,消元完成
//Debug();
//唯一解或者无穷解,k<=var
//var-k==0 唯一解;var-k>0 无穷多解,自由解的个数=var-k
//下面这几行很重要,保证秩内每行主元非0,且按对角线顺序排列
for(int i = 0; i <equ; ++i)//每一行主元素化为非零

if(!a[i][i])
{
int j;
for(j = i+1;j<var;++j)
if(a[i][j])
break;
if(j == var)
break;
for(int k = 0;k < equ; ++k)
swap(a[k][i],a[k][j]);
}
// ----处理保证对角线主元非0 且顺序,完成
free_num=k;
if (var-k>0) { dfs(var-1); return ans;}
if (var-k==0)//唯一解时,poj1753 本题没唯一解,当题目具体操作给出后,系数矩阵已
经固定了!
{
//下面是回带求解,当无穷多解时,最后几行为0
for(int i = k-1;i >= 0; --i)
{
int tmp = a[i][var] % 2;
for(int j = i+1;j < var; ++j) //x[i]取决于x[i+1]--x[var]啊,所以后面的解对前面的解有影
响。
if(a[i][j] != 0)
tmp = ( tmp - (a[i][j]*x[j])%2 + 2 ) % 2;
//if (a[i][i]==0) x[i]=tmp; //最后的空行时,即无穷解得
//else
x[i] = (tmp/a[i][i]) % 2; //上面的正常解
}
int sum=0;
for(int i=0;i<var;i++) sum+=x[i];
return sum;

}
}
int main()
{
int k,free_num;
char c1[20];
memset(a,0,sizeof(a));
memset(x,0,sizeof(x));
ans=1000000000;
for (int i=0;i<4;i++)
{
cin>>c1;
//构造系数矩阵A
for (int j=0;j<4;j++)
{
k = 4*i+j;
a[k][k]=1;
if (i>0) a[k][k-4]=1; //上
if (i<3) a[k][k+4]=1; //下
if (j>0) a[k][k-1]=1; //左
if (j<3) a[k][k+1]=1; //右
if (c1[j]=='b')
a[k][maxn] = 1;
}
}
for(int i=0;i<16;i++)
for(int j=0;j<=16;j++)
b[i][j]=a[i][j];
for(int k=0;k<16;k++)
b[k][16]^=1;
equ=var=16;
int j1=Gauss_1();
int min1=ans;
for(int i=0;i<16;i++)
for(int j=0;j<=16;j++)

a[i][j]=b[i][j];
ans=100000000;
int j2=Gauss_1();
int min2=ans;
if (j1==-1&&j2==-1) cout<<"Impossible"<<endl;
else
cout<<min(ans,min1)<<endl;
// cout << "Hello world!" << endl;
return 0;
}
对于2947 和2065 我是用下面的模板过的
下是高斯消元+同余方程的模板
#define maxn 80
int equ,var,prime;
char st[80];
int aa[maxn][maxn],x[maxn];
inline int abs1(int x)
{
if (x>=0) return x;
else
return -1*x;
}
inline int gcd(int a, int b)
{
int t;
while (b != 0)
{
t = b;
b = a % b;
a = t;

a[i][j]=b[i][j];
ans=100000000;
int j2=Gauss_1();
int min2=ans;
if (j1==-1&&j2==-1) cout<<"Impossible"<<endl;
else
cout<<min(ans,min1)<<endl;
// cout << "Hello world!" << endl;
return 0;
}
对于2947 和2065 我是用下面的模板过的
下是高斯消元+同余方程的模板
#define maxn 80
int equ,var,prime;
char st[80];
int aa[maxn][maxn],x[maxn];
inline int abs1(int x)
{
if (x>=0) return x;
else
return -1*x;
}
inline int gcd(int a, int b)
{
int t;
while (b != 0)
{
t = b;
b = a % b;
a = t;

}
return a;
}
inline int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
int extgcd(int a, int b, int & x, int & y){
if (b == 0) { x=1; y=0; return a; }
int d = extgcd(b, a % b, x, y);
int t = x; x = y; y = t - a / b * y;
return d;
}
int Gauss(){
int i,j,k;
int max_r , col , temp;
int LCM , GCD;
int ta,tb;
col = 0;
for(k=0 ; k<equ && col < var ; k++,col++)
{
max_r = k;
for(i=k+1 ; i<equ ; i++)
{ if(abs1(aa[i][col]) > abs1(aa[max_r][col]))max_r = i;}
if(max_r != k)
{ for(j=k ; j<var+1 ; j++)swap(aa[k][j],aa[max_r][j]);}
if(aa[k][col] == 0)
{ k--;continue;
}
for(i=k+1 ; i<equ ; i++)
{

if(aa[i][col] != 0)
{
LCM = lcm(abs1(aa[i][col]) , abs1(aa[k][col]));
ta = LCM/abs1(aa[i][col]) ;
tb = LCM/abs1(aa[k][col]);
if(aa[i][col] * aa[k][col] < 0)tb = -tb;
for(j=col ; j<var+1 ; j++)
{ aa[i][j] = (aa[i][j]*ta-aa[k][j]*tb) % prime;
}
}
}//for
}
for(i=var-1 ; i>=0 ; i--)
{
temp = aa[i][var];
for(j=i+1 ; j<var ; j++)
{ if(aa[i][j] != 0)temp =(temp - aa[i][j]*x[j]%prime) ;
}
temp = (temp%prime + prime) % prime;
GCD = extgcd(aa[i][i] , prime , x[i] , k);
x[i] = ( (x[i]*(temp/GCD) % prime) + prime) % prime;
}
return 0;
}
用以上的代码 很快把poj 2065 A 掉了
POJ 2947 高斯消元 解题报告
题目大意:有n 种装饰物,m 个已知条件,每个已知条件的描述如下:
p start end
a1,a2......ap (1<=ai<=n)
第一行表示从星期start 到星期end 一共生产了p 件装饰物(工作的天数为end-start+1+7*x,

加7*x 是因为它可能生产很多周),第二行表示这p 件装饰物的种类(可能出现相同的种类,
即ai=aj)。规定每件装饰物至少生产3 天,最多生产9 天。问每种装饰物需要生产的天数。
如果没有解,则输出“Inconsistent data.”,如果有多解,则输出“Multiple solutions.”,如果
只有唯一解,则输出每种装饰物需要生产的天数。
解题思路:高斯消元。设每种装饰物需要生产的天数为xi(1<=i<=n)。每一个条件就相当于
给定了一个方程式,假设生产1 类装饰物a1 件、2 类装饰物a2 件、i 类装饰物ai 件所花费
的天数为b,则可以列出下列方程:
a1*x1+a2*x2+...an*xn = b (mod 7)
这样一共可以列出m 个方程式,然后使用高斯消元来解此方程组即可。
#include <iostream>
#include <cstring>
#include <stdio.h>
using namespace std;
#define maxn 305
int equ,var,prime;
//char st[80];
char data[7][5]={"MON","TUE","WED","THU","FRI","SAT","SUN"};
int aa[maxn][maxn],x[maxn];
inline int abs1(int x)
{
if (x>=0) return x;
else
return -1*x;
}
inline int gcd(int a, int b)
{
int t;
while (b != 0)
{
t = b;

b = a % b;
a = t;
}
return a;
}
inline int lcm(int a, int b)
{
return a * b / gcd(a, b);
}
int extgcd(int a, int b, int & x, int & y){
if (b == 0) { x=1; y=0; return a; }
int d = extgcd(b, a % b, x, y);
int t = x; x = y; y = t - a / b * y;
return d;
}
int Gauss(){
int i,j,k;
int max_r , col , temp;
int LCM , GCD;
int ta,tb;
col = 0;
for(k=0 ; k<equ && col < var ; k++,col++)
{
max_r = k;
for(i=k+1 ; i<equ ; i++)
{ if(abs1(aa[i][col]) > abs1(aa[max_r][col]))max_r = i;}
if(max_r != k)
{ for(j=k ; j<var+1 ; j++)swap(aa[k][j],aa[max_r][j]);}
if(aa[k][col] == 0)
{ k--;continue;
}

for(i=k+1 ; i<equ ; i++)
{
if(aa[i][col] != 0)
{
LCM = lcm(abs1(aa[i][col]) , abs1(aa[k][col]));
ta = LCM/abs1(aa[i][col]) ;
tb = LCM/abs1(aa[k][col]);
if(aa[i][col] * aa[k][col] < 0)tb = -tb;
for(j=col ; j<var+1 ; j++)
{ aa[i][j] = (aa[i][j]*ta-aa[k][j]*tb) % prime;
aa[i][j]=(aa[i][j]%7+7)%7;
}
}
}//for
}
for(i=k;i<equ;i++)
{
if (aa[i][col]!=0) return -1;//无解
}
if (k<var) return var-k;// 无穷多解
for(i=var-1 ; i>=0 ; i--)
{
temp = aa[i][var];
for(j=i+1 ; j<var ; j++)
{ if(aa[i][j] != 0) temp =(temp - aa[i][j]*x[j]%prime) ;
//temp=temp-aa[i][j]*x[j];
}
temp = (temp%prime + prime) % prime;
GCD = extgcd(aa[i][i] , prime , x[i] , k);
x[i] = ( (x[i]*(temp/GCD) % prime) + prime) % prime;
// while(temp%aa[i][i])
// temp+=7;

//x[i]=temp/aa[/i][i];
//x[i]=(x[i]%7+7)%7;
while(x[i]<3) x[i]=x[i]+7;
//while(x[i]>9) x[i]=x[i]-7;
}
return 0;
}
//void init()
//{
// int n=var;
//}
int comp_1(char a[])
{
int j=0;
for(int i=0;i<7;i++)
if (strcmp(a,data[i])==0) j=i;
cout<<"s="<<j<<endl;
return j;
}
int main()
{
//`MON', `TUE', `WED', `THU', `FRI', `SAT' and `SUN'.
int m,n,k,typ;
int num1,num2;
char s1[5],s2[5];
prime=7;
while(scanf("%d%d",&m,&n))
{

if (m==0&&n==0) break;
var=m; //这个要注意,var!=equ 啊
equ=n;
//var=equ=m;
memset(aa,0,sizeof(aa));
memset(x,0,sizeof(x));
for(int i=0;i<n;i++)
{
scanf("%d",&k);
scanf("%s %s",s1,s2);
for(int i=0;i<7;i++)
{
if (strcmp(s1,data[i])==0)
num1=i;
if (strcmp(s2,data[i])==0)
num2=i;
}
int day=(num2-num1+1+7)%7;
// cout<<day<<endl;
for(int j=0;j<k;j++)
{
scanf("%d",&typ);
aa[i][typ-1]++;
aa[i][typ-1]%=7;
}
aa[i][m]=day;
}
int free_num=Gauss();
if (free_num==-1) cout<<"Inconsistent data."<<endl;
if (free_num>0) cout<<"Multiple solutions."<<endl;
if (free_num==0)
{

for(int i=0;i<var-1;i++)
cout<<x[i]<<" ";
cout<<x[var-1]<<endl;
}
}
//cout << "Hello world!" << endl;
return 0;
}
//本取余模板很好用,注意参数别犯低级错误
还有要补充的:
对于像1830 这样“连动“的开关的问题,采用逆向思维,以最终的灯列方程,不能以开始
的灯列方程;就是a[i]j[]的系数矩阵录入时,实际录入的是 a[j][i],就行了。
啥叫连动,就是1 个灯亮和不亮由很多灯控制,具体体会1830 题的文字描述,最后能把
HDU 的3364 和4200AC 掉,就算掌握“连动“了。
作业:把这9 个题都搞定,并尝试 同余的模板能用开始的那个模板来自己改写吗?
终极目标:举1 反3,掌握求解这类问题的本质,对于打亚洲赛的同学,要会使用double
型的高斯消元(矩阵值是整数,但解是双精度的),同时要能使用高斯+高精度AC 掉2008
亚洲哈尔滨的那道纯高消题,这是终极目标。
高消的考试1 周后进行!4 道简单题。
陈宇 2012/8/31

转载于:https://www.cnblogs.com/nealgavin/archive/2012/09/03/3206025.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值