ps.改了标题

魔板

  • 思路:按序消除变量,用当前行(i)【行i消\(x_i\)元素】,消后面的每一行的i元素
    最后按逆序回代值
    注意若有i~n行i元素系数都为0说明没有唯一解(其余x的解跟i元素有关),没事,跳过i元素不跳过i行继续算,最后判断系数全为0的情况下值是否为0

  • 代码:

#include<bits/stdc++.h>
using namespace std;
const int N=105;
double a[N][N],eps=1e-8;
int main() {
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++) for(int j=0;j<=n;j++)scanf("%lf",&a[i][j]);
	//校园 
	for(int i=0;i<n;i++) {		//j:当前的变量,i:第几行 
		int r=i;
		for(int k=i+1;k<n;k++) 
			if(fabs(a[k][i])>fabs(a[r][i])) r=k;
		if(fabs(a[r][i])<eps) {printf("No Solution");return 0;}
		if(r!=i) {
			for(int j=0;j<=n;j++) swap(a[r][j],a[i][j]);
		}
		for(int k=i+1;k<n;k++) {
			double s=a[k][i]/a[i][i];
			for(int l=0;l<=n;l++) {
				a[k][l]-=a[i][l]*s;
			}
		}
	}
	//回代
	for(int i=n-1;i>=0;i--) {
		a[i][n]=a[i][n]/a[i][i];
		for(int j=0;j<i;j++) {
			a[j][n]-=a[j][i]*a[i][n];
		}
	}
	for(int i=0;i<n;i++) printf("%.2lf\n",a[i][n]);
	return 0;
}

补充几道习题。

SETI

  • 思路:一道版题,但因为我上课听的不够认真而误解。
    同余方程下成立直接最小正整数成立即可Qwq
    那就很水了,不贴代码

球形空间产生器

  • 思路:我不知道为什么可以相邻的来搞在一起就会对。总之假如这样,用数学推导就很容易了。

中央暖气

  • 思路:文字游戏,然后引进了一类新的xor类型的高斯消元,其实还比浮点数好写些.
  • 代码(xor)
//我是傻逼  我是傻逼  我是傻逼  
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=305;
int n;
bool a[N][N];
bool Guass() {
	for(int i=1;i<=n;i++) {
		int r=i;
		while(!a[r][i]&&r<n) r++;
		if(!a[r][i]) {return false;}
		if(r!=i) {
			for(int j=1;j<=n+1;j++) swap(a[r][j],a[i][j]);		
		}  
		for(int j=i+1;j<=n;j++) {
			if(a[j][i]) {
				for(int k=1;k<=n+1;k++) a[j][k]^=a[i][k];
			}
		}
	}
	for(int i=n;i;i--) {
		for(int j=1;j<i;j++) {
			a[j][n+1]^=a[j][i]*a[i][n+1];
		}
	}
	for(int i=1;i<=n;i++) {
		if(a[i][n+1]) printf("%d ",i);
	}
	return true;
}
int main() {
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		int x;
		while(1) {
			scanf("%d",&x);
			if(x==-1) break;
			a[x][i]=1;
		}
	}
	for(int i=1;i<=n;i++) a[i][n+1]=1;
	if(!Guass())printf("No solution");
	return 0;
}

ps.之所以sb因为没初始化调了30min

开关问题

  • 思路:这道考察对自由元的理解:
    1.出现自由元只会lead to two sides无解或者多解
    2.若多解,无论自由元取值什么值,非自由元依旧有解。(即自由元与非自由元无明显联系)
    然后,这道题就答案:2^自由元的个数

手机游戏

  • 思路:这个也写了好久,就很烦。(震惊!原因竟然是for循环没有枚举到0,而是枚举到了1)
    思路就是二进制枚举自由元选取状态,一个一个带回取消元。

  • 代码:

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
char s[N][N];
bool is_free[N],tmp[N],a[N][N];
int lst,b[N][N],n,Free[N],num,dir[7][2]={{0,0},{1,0},{-1,0},{0,-1},{0,1}};
bool Goose() {
	int i,j;
	for(i=0,j=0;i<n&&j<n;) {
		int r=i;
		while(!a[r][j]&&r<=n) r++;
		if(r>n) {is_free[j]=1;Free[num++]=j++;continue;}
		if(r!=i) for(int k=0;k<=n;k++)swap(a[i][k],a[r][k]);
		for(int k=i+1;k<n;k++) {
			if(a[k][j]) {
				for(int l=0;l<=n;l++) a[k][l]^=a[i][l];
			}
		}
		i++,j++;
	}
	for(int k=i;k<n;k++) if(a[k][n]){return false;}
	lst=i;
	return true;
}
int Freedom() {
	int ans=1e9;
	for(int s=0;s<(1<<num);s++) {
		for(int i=0;i<n;i++) tmp[i]=a[i][n];
		int res=0;
		for(int i=0;i<num;i++) {
			if((1<<i)&s) {
				res++;
				for(int k=0;k<n;k++) {
					tmp[k]^=a[k][Free[i]];
				}
			}
		}
		int i=lst-1;
		for(int j=n-1;j>=0;j--) {
			if(is_free[j])continue;
			res+=tmp[i];
			for(int k=0;k<i;k++) {
				tmp[k]^=tmp[i]*a[k][j];
			}
			i--;
		}
		ans=min(ans,res);
	}
	return ans;
}
int main() {
	int q;
	scanf("%d",&q);
	for(int i=0;i<q;i++) scanf("%s",s[i]);
	for(int i=0;i<q;i++)for(int j=0;j<q;j++)b[i][j]=n++;
	for(int i=0;i<q;i++)for(int j=0;j<q;j++)a[b[i][j]][n]=(s[i][j]=='w');
	for(int i=0;i<q;i++)for(int j=0;j<q;j++) {
		for(int d=0;d<5;d++) {
			int x=i+dir[d][0],y=j+dir[d][1];
			if(x<0||y<0||x>=q||y>=q)continue;
			a[b[x][y]][b[i][j]]=1;
		}
	}
	if(!Goose()) printf("inf");
	else {
		int ans=Freedom();
		printf("%d",ans);
	}
	return 0;
}

[USACO3.2.4】Feed Ratios饲料调配

  • 思路:告诉我们两件事
    1.若多了一个变量别急着看题解(我很后悔),直接暴力枚举这个变量即可。
    2.有毒瘤出题人卡高斯消元弄出了x.9999..这种数据,然后fabs((int)x.9999-x.9999)=1然后就挂了
    解决方法就是一切浮点数+eps(越小越好)

  • 代码:

//这题的浮点数误差剧坑
//(int)4.9999999...=4 
#include<bits/stdc++.h>
using namespace std;
int b[5][5];
double a[5][5],eps=1e-14;
bool Guass() {
	for(int i=1;i<=3;i++) {
		int r=i;
		for(int k=i+1;k<=3;k++) 
			if(fabs(a[k][i])>fabs(a[r][i])) r=k;
		if(fabs(a[r][i])<eps) {return false;}
		if(r!=i) {
			for(int j=1;j<=4;j++) swap(a[r][j],a[i][j]);
		}
		for(int k=i+1;k<=3;k++) {
			double s=a[k][i]/a[i][i];
			for(int l=1;l<=4;l++) {
				a[k][l]-=a[i][l]*s;
			}
		}
	}
	for(int i=3;i>=1;i--) {
		a[i][4]=a[i][4]/a[i][i];
		for(int j=1;j<i;j++) {
			a[j][4]-=a[j][i]*a[i][4];
		}
	}
	return true;
}
int main() {
	int x,y,z,X,Y,Z;
	scanf("%d%d%d",&x,&y,&z);
	for(int i=1;i<=3;i++) {
		for(int j=1;j<=3;j++) {
			scanf("%d",&b[i][j]);
		}
	}
	for(int k=1;k<=100;k++) {
		for(int j=1;j<=3;j++) {
			for(int i=1;i<=3;i++) {
				a[j][i]=b[i][j];
			}
		}
		X=x*k,Y=y*k,Z=z*k;
		a[1][4]=X,a[2][4]=Y,a[3][4]=Z;
		if(Guass()) {
			bool flag=true;
			for(int i=1;i<=3;i++) {
				if(a[i][4]-(int)a[i][4]>eps&&(int)(a[i][4]+eps)==(int)a[i][4]||a[i][4]+eps<0) {flag=false;break;}
			}	
			if(flag) {
				for(int i=1;i<=3;i++) printf("%d ",(int)(a[i][4]+eps));
				printf("%d",k);
				return 0;
			}
		}
	}
	printf("NONE");
	return 0;
}

外星千足虫

  • 思路:在每次找到非零行时取max,注意用bitset优化一个O(W)
    注意bitset是一个二进制,只能整体运算,但可以取单体的值。
  • 代码:
#include<bits/stdc++.h>
using namespace std;
const int N=2e3+5;
bitset<N> a[N];
char s[N];
int k,n,m,b[N];
void Guass() {
	for(int i=0;i<n;i++) {
		int r=i;
		while(r<m&&!a[r][i]) r++;
		if(r==m) {printf("Cannot Determine");exit(0);}
		k=max(k,r);
		if(i!=r) swap(a[i],a[r]),swap(b[i],b[r]);
		for(int j=i+1;j<m;j++) {
			if(a[j][i]) {
				a[j]^=a[i],b[j]^=b[i];
			}
		}
	}
	for(int i=n-1;i>=0;i--) {
		for(int j=0;j<i;j++) {
			b[j]^=b[i]*a[j][i];
		}
	}
}
int main() {
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++) {
		int x;
		scanf("%s%d",s,&x);
		for(int j=0;j<n;j++) a[i][j]=s[j]-'0';
		b[i]=x;
	}
	Guass();
	printf("%d\n",k+1);
	for(int i=0;i<n;i++) {
		if(b[i]) printf("?y7M#\n");
		else printf("Earth\n");
	}
	return 0;
}

【CQOI2014】和谐矩阵【毒瘤版】

一切的源头就是NKOJ男鞋的spj,然后就进阶成了一道思维题(字典)

  • 思路:或许其它人都是用的暴力+贪心,但是我是直接贪心(没有任何暴力,跑得飞快)
    首先,我们容易想到倒着消元,这样越前面的数只要不是一定确定,被定为自由元的优先级就更高
    所以回代的顺序就是顺着的。
    其次,自由元的性质是该自由元后面的行都为0,而我们是倒着消元的,因此自由元不影响前面的取值(字典序),所以我们直接将所有自由元取0。
    你会发现矩阵全零了!!!
    最后,我们贪心:将最后一个自由元赋为1,然后顺势回代,跟原高斯消元的代码相近,然后就AC啦。
    贪心就用“其次……”这个性质非常的好证明。
  • 代码:
1485 B
源代码
#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int a[N][N];
bool is_free[N];
int h,l,fst,b[N][N],n,cnt=0,dir[7][2]={{0,0},{1,0},{-1,0},{0,1},{0,-1}};
void Print() {
	for(int i=0;i<n;i++) {
		if(i&&i%l==0) printf("\n");
		if(i==fst) printf("1 ");
		else if(is_free[i]) printf("0 ");
		else printf("%d ",a[i][n]);
	}
}
void Guass() {
	int i,j,r;
	for(i=n-1,j=n-1;i>=0&&j>=0;) {
		r=i;
		while(r>=0&&!a[r][j]) {r--;}
		if(r<0) {if(!fst)fst=j;is_free[j]=1,--j;continue;}
		if(i!=r) swap(a[i],a[r]);
		for(int k=i-1;k>=0;k--) {
			if(a[k][j]) {
				for(int l=0;l<=n;l++) a[k][l]^=a[i][l];
			}
		}
		--i,--j;
	}
//	Print();
//	Print2();
	i++; j++;
	while(i<n&&j<n) {
//		printf("%d %d\n",i,j);
		if(fst==j) {
			for(int k=i;k<n;k++) if(a[k][j])a[k][n]^=1;
			j++; continue;
		} 
		else if(is_free[j]) {j++;continue;}
		else if(a[i][n]) {
			for(int k=i+1;k<n;k++) {
				a[k][n]^=a[i][n]*a[k][j];
			}
		}
		i++,j++;
//		Print();
//		Print2();
	}
}
int main() {
	scanf("%d%d",&h,&l);
	for(int i=1;i<=h;i++) for(int j=1;j<=l;j++) b[i][j]=cnt++;
	for(int i=1;i<=h;i++) for(int j=1;j<=l;j++) {
		for(int t=0;t<5;t++) {
			int x=i+dir[t][0],y=j+dir[t][1];
			if(x<1||y<1||x>h||y>l) continue;
			a[b[i][j]][b[x][y]]=1;
		}
	}
	n=h*l;
	Guass();
	Print();
	return 0;
}