高斯消元
模板(P2455)
#include<bits/stdc++.h>
using namespace std;
const int N=110;
const double eps=1e-12;
int n,id[N],line;
double a[N][N];
bool zero(double x)
{
return fabs(x)<eps;
}
void gauss()
{
line=1;
for(int i=1; i<=n; i++)
{
int mx=line;
for(int j=line+1; j<=n; j++)
if(fabs(a[mx][i])<fabs(a[j][i]))
mx=j;
if(zero(a[mx][i]))
continue;
for(int j=1; j<=n+1; j++)
swap(a[line][j],a[mx][j]);
for(int j=1; j<=n; j++)
{
if(line==j)
continue;
double tmp=a[j][i]/a[line][i];
for(int k=1; k<=n+1; k++)
a[j][k]-=a[line][k]*tmp;
}
id[i]=line++;
}
}
void NIE(int x)
{
printf("%d",x);
exit(0);
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
for(int j=1; j<=n+1; j++)
scanf("%lf",&a[i][j]);
gauss();
for(int i=1; i<=n; i++)
if(!id[i])
id[i]=line++;
for(int i=1; i<=n; i++)
if(zero(a[id[i]][i]) && !zero(a[id[i]][n+1]))
NIE(-1);
for(int i=1; i<=n; i++)
if(zero(a[id[i]][i]) && zero(a[id[i]][n+1]))
NIE(0);
for(int i=1; i<=n; i++)
{
double ans=a[id[i]][n+1]/a[id[i]][i];
if(zero(ans))
ans=0.00;
printf("x%d=%.2lf\n",i,ans);
}
return 0;
}
一些特殊情况
-
若存在系数全为
,常数不为 的行,则方程组无解 -
若系数不全为
的行恰好有 个,则说明主元有 个,方程有唯一解 -
若系数不全为
的行有 个,则说明主元有 个,自由元有 个,方程组有无穷多个解
Part 2:一点点习题
[USACO09NOV] Lights G
(题目传送门)
题目大意
给出一张
你可以操作任意一个点,操作结束后该点以及所有与该点相邻的点的状态都会改变,由
你需要求出最少的操作次数,使得在所有操作完成之后所有
解题思路
-
设
表示第 个点是否操作, 表示无, 表示有 -
设
表示第 个点和第 个点是否联通 -
那么根据题目大意,我们可以构造异或方程组
- 之后,我们使用高斯消元可得出一个上三角矩阵,可能会存在自由元。如果不存在,直接统计答案即可。如果存在,我们从后面开始 dfs,对于所有的自由元,赋值成
或 继续 dfs 即可
#include<bits/stdc++.h>
using namespace std;
const int N=45;
const double eps=1e-12;
int n,m,val[N];
int a[N][N],ans=N;
bool gauss()
{
bool flag=1;
for(int i=1; i<=n; i++)
{
int mx=i;
for(mx=i; mx<=n; mx++)
if(a[mx][i])
break;
if(mx>n)
{
flag=0;
continue;
}
for(int j=1; j<=n+1; j++)
swap(a[i][j],a[mx][j]);
for(int j=1; j<=n; j++)
{
if(j==i || !a[j][i])
continue;
for(int k=i; k<=n+1; k++)
a[j][k]^=a[i][k];
}
}
return flag;
}
void dfs(int x,int num)
{
if(num>=ans)
return;
if(x==0)
ans=num;
if(a[x][x])
{
val[x]=a[x][n+1];
for(int i=x+1; i<=n; i++)
val[x]^=(a[x][i]&val[i]);
if(val[x])
dfs(x-1,num+1);
else
dfs(x-1,num);
}
else
{
val[x]=0;
dfs(x-1,num);
val[x]=1;
dfs(x-1,num+1);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
a[x][y]=a[y][x]=1;
}
for(int i=1; i<=n; i++)
a[i][i]=a[i][n+1]=1;
if(gauss())
{
ans=0;
for(int i=1; i<=n; i++)
ans+=a[i][n+1];
printf("%d",ans);
}
else
{
dfs(n,0);
printf("%d",ans);
}
return 0;
}
[JSOI2012] 始祖鸟
(题目传送门)
(双倍经验)
题目大意
现在有
聚会的地点分为两处,一处在上游,一处在下游。对于每一处聚会场所,都必须满足对于在这个聚会场所中的始祖鸟,有恰好有偶数个自己认识的朋友与之在同一个聚会场所中。当然,每一只始祖鸟都必须在两处聚会场所之一。
现在需要你给出一种安排方式。你只需要给出在上游的始祖鸟编号,如果有多组解,请输出任何一组解。
解题思路
-
设第
只鸟的朋友为 , 表示第 只鸟的状态。 表示去上游, 表示去下游。因为涉及到奇偶,所以我们按朋友数量的奇偶性来分类 -
若
是偶数-
若第
只鸟去上游,因为去上游的朋友数量是偶数个,所以有 -
若第
只鸟去下游,那么显然去上游的朋友数量也是偶数个(偶数 偶数 偶数),所以仍然
-
-
若
是奇数-
若第
只鸟去上游,仍然是 -
若第
只鸟去下游,此时方程变了,变成 -
考虑将两个方程变成同一个,观察到如果方程同时异或上
的话,那么等号右边就都是 ,所以将方程改成
-
-
bitset
优化高斯消元即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2010;
int n;
bitset <N> a[N];
void NIE()
{
printf("Impossible");
exit(0);
}
void gauss()
{
for(int i=1; i<=n; i++)
{
int mx=i;
for(mx=i; mx<=n; mx++)
if(a[mx][i])
break;
if(mx>n)
continue;
swap(a[i],a[mx]);
for(int j=1; j<=n; j++)
{
if(j==i || !a[j][i])
continue;
a[j]^=a[i];
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
int m,x;
scanf("%d",&m);
for(int j=1; j<=m; j++)
{
scanf("%d",&x);
a[i][x]=1;
}
if(m&1)
a[i][i]=1,a[i][n+1]=1;
}
gauss();
int cnt=0;
for(int i=1; i<=n; i++)
{
if(!a[i][i] && a[i][n+1])
NIE();
if(a[i][n+1]&1)
cnt++;
}
printf("%d\n",cnt);
for(int i=1; i<=n; i++)
{
if(a[i][n+1])
printf("%d ",i);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?