最后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]);}
 elseprintf("%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