数学专题训练4

这次是矩阵与行列式

题目一:bzoj1013

我看到这题时反正没什么感觉。。。

其实对于球来说。。其上每一个点到球心的距离都是相等的。。。于是就可以根据这个来列方程

设球心为(x1,x2,...)对于点(a1,a2,...),(b1,b2,...)  有Σ(ai-xi)^2=Σ(bi-xi)^2

看起来很麻烦。。因为方程有二次项

但其实这个式子是可以打开的。。。打开后变成Σ2*(bi-ai)xi = Σbi^2-Σai^2 二次项被消掉了,直接就是线性方程(我当时居然觉得ai^2这些是二次项没消掉纠结了半天,真是太傻×了)

然后确定一个点出来。。其他所有点都与这个点一起建立一个线性方程,组成线性方程组。。。

然后直接高斯消元就搞定了...

#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int n;
double A[10+5][10+5];
double zb[10+5][10+5],fsum=0;
void gauss()
{
	for(int i=1;i<=n;i++)
	{
		int maxt=i;
		for(int j=i+1;j<=n;j++)
		{
			if(fabs(A[j][1])>fabs(A[maxt][1]))maxt=j;
		}
		if(maxt!=i)for(int j=1;j<=n+1;j++)swap(A[i][j],A[maxt][j]);
		for(int j=i+1;j<=n;j++)
		{
			for(int k=n+1;k>=i;k--)A[j][k]-=A[i][k]*A[j][i]/A[i][i];
		}
	}
	for(int i=n;i>=1;i--)
	{
		for(int j=i+1;j<=n;j++)A[i][n+1]-=A[i][j]*A[j][n+1];
		A[i][n+1]/=A[i][i];
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lf",&zb[0][i]);
		fsum=fsum+zb[0][i]*zb[0][i];
	}
	for(int i=1;i<=n;i++)
	{
		double suma=0;
		for(int j=1;j<=n;j++)
		{
			scanf("%lf",&zb[i][j]);
			suma+=zb[i][j]*zb[i][j];
			A[i][j]=2*(zb[i][j]-zb[0][j]);
		}
		A[i][n+1]=suma-fsum;
	}
	gauss();
	for(int i=1;i<n;i++)printf("%.3f ",A[i][n+1]);
	printf("%.3f\n",A[n][n+1]);
	return 0;
}

题目二:LA3704

这个题嘛。。。线性递推关系。。。一看就是矩阵乘法优化

设 矩阵v[t+1]=A*v[t],然后v[k]就等于 A^k *v[0]

但是仔细分析下矩阵乘法优化后的复杂度为 O(n^3logk),还是T得比较爽

这里要用到一个性质:A这个矩阵是一个循环矩阵(下一行由上一行依次移动以为得到)

而两个循环矩阵的乘积依旧是循环矩阵 。。所以我们在求A^k时,只计算和保存第一行就行了。。。

所以求A^k这个过程的复杂度就变成了 O(n^2logk).

至于矩阵怎么设计。。。应该不是很难的问题,实在解决不了的话可以给我留言。。我在这几个月会几乎天天都会来的

 

#include<cstdlib>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
int n,m,d,k;
int M[500+10][1];
int ele[500+10];
int temp[500+10];
int temp2[500+10];
int temp4[500+10];
int temp3[500+10][1];
void mul2(int a[],int b[][1])
{
	memcpy(temp3,b,sizeof(temp3));
	memset(b,0,sizeof(temp3));
	for(int i=0;i<n;i++)
	{
		for(int k=0;k<n;k++)b[i][0]=(b[i][0]+((long long)a[((k-i)%n+n)%n]*temp3[k][0])%m)%m;
	}
}
void mul(int a[],int b[])
{
	memcpy(temp2,a,sizeof(temp2));
	memcpy(temp4,b,sizeof(temp4));
	memset(a,0,sizeof(temp));
	for(int i=0;i<n;i++)
	{
		for(int k=0;k<n;k++)a[i]=(a[i]+((long long)temp2[k]*temp4[((i-k)%n+n)%n])%m)%m;
	}
}
void qpow(int x)
{
	memcpy(temp,ele,sizeof(temp));
	memset(ele,0,sizeof(ele));
	ele[0]=1;
	while(x)
	{
		if(x&1)mul(ele,temp);
		mul(temp,temp);
		x>>=1;
	}
}
int main()
{
	while(scanf("%d%d%d%d",&n,&m,&d,&k)!=EOF)
	{
	for(int i=0;i<n;i++)
	{
		scanf("%d",&M[i][0]);
	}
	for(int i=0;i<n;i++)
	{
		if(i-0<=d||n-i<=d)ele[i]=1;
	}
	qpow(k);
	mul2(ele,M);
	for(int i=0;i<n-1;i++)printf("%d ",M[i][0]);
	printf("%d\n",M[n-1][0]);
	memset(M,0,sizeof(M));
	memset(temp,0,sizeof(temp));
	memset(temp4,0,sizeof(temp4));
	memset(temp2,0,sizeof(temp2));
	memset(temp3,0,sizeof(temp3));
	memset(ele,0,sizeof(ele));
	}
	return 0;
}

题目三:Uva 10828

这道题有点高端。。期望+线性方程组

假设点i的期望执行次数为xi

那么xi=xa/da+xb/db+xc/dc+..(a,b,c为xi的前驱节点,di为点i的出度,意思就是:i点的期望执行次数=从a点走过来的概率*a点的期望执行次数+...)

例外是x1=xa/da+xb/db+xc/dc+..+1(因为他一开始会执行一次。。相当于有一个0号节点做前驱)

列出方程组然后消元。。。貌似解决了

但是没有。。。因为有的点不会被执行。。有的点会被无限执行(比如自环),在方程组中体现为多余方程或者矛盾方程

高斯消元有回带过程。。这里不知道的量就没法回带了

所以直接用高斯约当消元。。。不用回带

最后剩下的方程如果是矛盾的则说明所对应的这个变量为inf,如果是多余方程的话就是所对应的节点不会被执行(这句话是我自己的理解。。。没有太细想,可能不太对)

白书上是这么说的:当A[i][i]=A[i][n]=0时 即方程为 xi*0=0时 xi为0  ,当A[i][i]=0&&A[i][n]>0 即方程为xi*0=s(s>0)时xi为inf。。。除此因为有无穷大的变量而不能找出解的方程对应的xi也是无穷大(我认为是由于可以从 inf节点 走来。。。但好像不太解释得通,求大牛指导!)

#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const double eps=1e-8;
int n;
bool w[100+10][100+10];
int du[100+10];
double A[100+10][100+10];
bool inf[100+10];
void gauss_jordan()
{
	for(int i=1;i<=n;i++)
	{
		int maxt=i;
		for(int j=i+1;j<=n;j++)
		{
			if(fabs(A[j][i])>fabs(A[maxt][i]))maxt=j;
		}
		if(abs(A[maxt][i])<eps)continue;
		if(i!=maxt)for(int j=1;j<=n+1;j++)swap(A[i][j],A[maxt][j]);
		for(int j=1;j<=n;j++)
		{
			if(i!=j)
			{
				for(int k=n+1;k>=i;k--)
				{
					A[j][k]-=A[j][i]/A[i][i]*A[i][k];
				}
			}
		}
	}
}
int main()
{
	int cas=0;
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)break;
		memset(w,0,sizeof(w));
		memset(du,0,sizeof(du));
		memset(A,0,sizeof(A));
		memset(inf,0,sizeof(inf));
		cas++;
		int a,b;
		while(scanf("%d%d",&a,&b)!=EOF)
		{
			if(a==0&&b==0)break;
			w[a][b]=1;
			du[a]++;
		}
		A[1][n+1]=1;
		for(int i=1;i<=n;i++)
		{
			A[i][i]=1;
			for(int j=1;j<=n;j++)
			{
				if(w[j][i])
				A[i][j]-=1.0/du[j];
			}
		}
		gauss_jordan();
		for(int i=n;i>=1;i--)
		{
			if(fabs(A[i][i])<eps&&fabs(A[i][n+1])>eps)inf[i]=true;
			for(int j=i+1;j<=n;j++)
			{
				if(fabs(A[i][j])>eps&&inf[j])inf[i]=true;
			}
		}
		printf("Case #%d:\n",cas);
		int q;
		scanf("%d",&q);
		for(int i=1;i<=q;i++)
		{
			int u;
			scanf("%d",&u);
			if(inf[u])printf("infinity\n");
			else printf("%.3f\n",fabs(A[u][u])<eps?0.0:A[u][n+1]/A[u][u]);
		}
	}
	return 0;
}

题目四:Uva11542

这个我也比较晕。。。就说一句:异或方程组的消元过程进行几行 大概就是 确定了多少个未知数吧(说错了不要打我)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值