高斯消元

写在前面

顾名思义,解多元一次方程组用的 \(O(n^3)\)
实在不行枚举一些未知数。
解决多个条件相互限制的情况,比如一些成环的dp。

模板

先上模板,这里用的整数的模板,浮点数的话应该更好写。

题目描述

给出一个线性方程组,有n个方程组,m个未知数。解这个线性方程组。测试数据保证如果有解,一定是整数解。

输入格式

第1行:2个整数n和m,(n, m <=15,且n不一定等于m)
接下来n行,每行m+1个整数,表示一个方程的m个未知数的系数和常数(绝对值不超过10)

输出格式

如果无解,输出“No solution”。
如果有唯一解,输出m行,每行一个未知数的值,保证解是整数,而且解的绝对值不超过10。格式见样例。
如果有无穷解,输出m行,如果未知数有确定解,直接输出。如果是自由变元,输出"not determined",格式见样例。
数据保证没有浮点数解。

样例

样例输入1
3 3
2 1 2 11
1 2 1 7
2 3 1 10

样例输出1

X[1] = 2
X[2] = 1
X[3] = 3

样例输入2

3 3
3 1 2 11
1 3 2 13
1 3 2 13

样例输出2

X[1] not determined
X[2] not determined
X[3] not determined

样例输入3

4 4
1 3 1 2 12
1 2 1 3 12
1 3 2 1 12
1 3 2 1 11

样例输出3

No solution

code

#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
int n,m,l;
int a[20][20],x[20];
bool v[20],flag=1;
void debug(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m+1;j++)
			printf("%d ",a[i][j]);
		printf("\n");
	}
	printf("--------\n");
}
void xy(int x,int y){
	if(abs(a[x][l])<abs(a[y][l]))
		swap(a[x],a[y]);
	while(a[y][l]){
		int k=a[x][l]/a[y][l];
		for(int i=l;i<=m+1;i++)
			a[x][i]-=a[y][i]*k;
		if(abs(a[x][l])<abs(a[y][l]))
			swap(a[x],a[y]);
	}
}
void gs(){
	l=1;
	for(int i=1;i<=n&&l<=m;i++,l++){
		int t=i;
		for(int j=i+1;j<=n;j++)
			if(abs(a[j][l])>abs(a[t][l]))
				t=j;
		swap(a[t],a[i]);
		if(a[i][l]==0){
			i--;
			continue;
		}
		for(int j=i+1;j<=n;j++)
			xy(i,j);
//		debug();
	}
	for(int i=n;i>=1;i--){
		int sum=0;
		bool f=0; 
		for(int j=m;j>=1;j--)
			if(a[i][j]){
				f=1;
				if(!v[j])
					sum++;
				else
					a[i][m+1]-=x[j]*a[i][j]; 
			}
		if(!f&&a[i][m+1]){
			flag=0;
			return ;
		}
		if(sum==1)
			for(int j=m;j>=1;j--)
				if(a[i][j]&&!v[j]){
					v[j]=1;
					x[j]=a[i][m+1]/a[i][j];
					break;
				}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m+1;j++)
			scanf("%d",&a[i][j]);
	gs();
	if(!flag){
		printf("No solution");
		return 0;
	}
	for(int i=1;i<=m;i++)
		if(v[i])
			printf("X[%d] = %d\n",i,x[i]);
		else
			printf("X[%d] not determined\n",i);
}

[HNOI2013] 游走

题目描述

给定一个 \(n\) 个点 \(m\) 条边的无向连通图,顶点从 \(1\) 编号到 \(n\),边从 \(1\) 编号到 \(m\)

小 Z 在该图上进行随机游走,初始时小 Z 在 \(1\) 号顶点,每一步小 Z 以相等的概率随机选择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数。当小 Z 到达 \(n\) 号顶点时游走结束,总分为所有获得的分数之和。 现在,请你对这 \(m\) 条边进行编号,使得小 Z 获得的总分的期望值最小。

输入格式

第一行是两个整数,分别表示该图的顶点数 \(n\) 和边数 \(m\)

接下来 \(m\) 行每行两个整数 \(u,v\),表示顶点 \(u\) 与顶点 \(v\) 之间存在一条边。

输出格式

输出一行一个实数表示答案,保留三位小数。

样例 #1

样例输入 #1

3 3
2 3
1 2
1 3

样例输出 #1

3.333

数据规模

  • 对于 \(30\%\) 的数据,保证 \(n\leq 10\)
  • 对于 \(100\%\) 的数据,保证 \(2\leq n \leq 500\)\(1 \leq m \leq 125000\)\(1 \leq u, v \leq n\),给出的图无重边和自环,且从 \(1\) 出发可以到达所有的节点。

题解

首先考虑每一条边,如果我们知道每一条边的期望经过次数,那么就贪心地期望次数大的边选较小的边权。
对于每一条有向边边 \((u,v)\) ,设它的期望经过次 \(g_i=\frac{f_u}{in_u}+\frac{f_v}{in_v}(u \neq n)\)\(f_i\) 为第 \(i\) 个点经过的期望,\(in_i\) 为度数。

\[f_1=\sum_{(i,j),j\neq n} \frac{f_j}{in_j}+1 \]

\[f_i=\sum_{(i,j),j\neq n} \frac{f_j}{in_j}(i\neq 1,i\neq n) \]

\[f_n=1 \]

考虑移项之后直接进行高斯消元。

code

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=505;
const int M=125000+5;
int n,m,cnt=1,in[N],head[N],l;
bool v[N];
double g[N][N],f[N],esp=1e-13,ans,w[M];
struct edge{
	int u,v,next;
}e[M<<1];
void add(int u,int v){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].next=head[u];
	head[u]=cnt;
}
void ff(int x){
	if(x==n){
		f[x]=1;
		return ;
	}
	for(int i=head[x];i;i=e[i].next){
		int to=e[i].v;
		if(to==n)
			continue;
		g[x][to]=1.0/in[to];
	}
	g[x][x]=-1;
	if(x==1)
		g[x][n]=-1;
}
void xy(double *x,double *y){
	double k=y[l]/x[l];
	for(int i=l;i<=n;i++)
		y[i]-=x[i]*k;
}
void gs(){
	l=1;
	for(int i=1;i<n&&l<n;i++,l++){
		int t=i;
		for(int j=i;j<n;j++)
			if(abs(g[j][l])-abs(g[t][l])>esp)
				t=j;
		swap(g[t],g[i]);
		if(abs(g[i][l])<esp){
			i--;
			continue;
		}
		for(int j=i+1;j<n;j++)
			xy(g[i],g[j]);
	}
	for(int i=n-1;i>=1;i--)
		for(int j=n-1;j>=1;j--)
			if(abs(g[i][j])>esp){
				if(v[j])
					g[i][n]-=g[i][j]*f[j];
				else{
					f[j]=g[i][n]/g[i][j];
					v[j]=1;
					break;
				}
			}
}
int main(){
	int u,v;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
		in[u]++;in[v]++;
	}
	for(int i=1;i<=n;i++)
		ff(i);
	gs();
	for(int i=2;i<=cnt;i+=2){
		u=e[i].u;
		v=e[i].v;
		if(u!=n)	w[i/2]+=f[u]/in[u];
		if(v!=n)	w[i/2]+=f[v]/in[v];
	}
	sort(w+1,w+1+m);
	for(int i=1;i<=m;i++)
		ans+=w[i]*(m+1-i);
	printf("%.3lf",ans);
} 
posted @ 2023-08-06 22:19  _LLD  阅读(30)  评论(0)    收藏  举报