UVA10828 - Back to Kernighan-Ritchie

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1769

题解

黑心?出题人qwq(就不能写个spj吗
这题是高斯消元,每个点的期望次数等于它的所有前驱的(期望次数/前驱的出度)之和, 1 1 1号结点还要 + 1 +1 +1
由于是任意的图,所以很容易发现这个东西没法 d p dp dp
考虑求解我列出的方程组,有些变量是解不出来的
比如下面这个情形:
1 → 2 1\rightarrow2 12
2 → 1 2\rightarrow1 21
3 → 1 3\rightarrow1 31
列出方程组:
{ e 1 = 1 + e 2 + e 3 e 2 = 0.5 e 1 e 3 = 0.5 e 1 \begin {cases} e_1=1+e_2+e_3\\ e_2=0.5e_1\\ e_3=0.5e_1 \end {cases} e1=1+e2+e3e2=0.5e1e3=0.5e1
消元得到了 0 = 1 0=1 0=1这样的等式
这样的情况下我确实得不到一个有界量来表示 e 1 , e 2 , e 3 e_1,e_2,e_3 e1,e2,e3
在实际情况中,这样的点应该是输出 i n f inf inf
用高斯消元作处理之后,这样的变量表现出来的特点是系数为 0 0 0且常数项不等于 0 0 0
这题有个精度的问题:
不要把 e i e_i ei的值提前算出来,因为算的时候你要除以系数,假如没除以系数之前常数项存的是 − 0.0000000000001 -0.0000000000001 0.0000000000001,而实际上这里应该是 0 0 0,如果用 e p s eps eps判一下,你输出的是 0 0 0(正确√),但是假设系数很小,等于 0.00000000001 0.00000000001 0.00000000001,那么你做完除法之后再判 e p s eps eps,你就会认为这个数字不等于 0 0 0,从而你输出了 − 0.000 -0.000 0.000(错误×)
出题人就不会写一个 s p j spj spj么qwq

代码

#include <bits/stdc++.h>
#define eps 1e-8
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define maxn 110
#define base 2000
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
int read(int x=0)
{
	int c, f=1;
	for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
	for(;isdigit(c);c=getchar())x=x*10+c-48;
	return f*x;
}
int cd[maxn], N, road[maxn][maxn], inf[maxn];
double mat[maxn][maxn], ans[maxn];
void gauss(double (*m)[maxn], int n)
{
	int i, j, k, r;
	for(i=1;i<=n;i++)
	{
		r=i;
		for(j=i+1;j<=n;j++)if(fabs(m[j][i])>fabs(m[r][i]))r=j;
		if(fabs(m[r][i])<eps)continue;
		for(j=1;j<=n+1;j++)swap(m[i][j],m[r][j]);
		for(j=1;j<=n;j++)
			if(j!=i)
			{
				for(k=n+1;k>=i;k--)m[j][k]-=m[j][i]/m[i][i]*m[i][k];
			}
	}
}
void init()
{
	int i, j, a, b;
	cl(inf), cl(mat), cl(cd), cl(ans), cl(road);
	while(a=read(), b=read(), a)
	{
		road[a][b]=1;
	}
	for(i=1;i<=N;i++)for(j=1;j<=N;j++)if(i!=j and road[i][j])cd[i]++;
	for(i=1;i<=N;i++)mat[i][i]=1;
	for(i=1;i<=N;i++)for(j=1;j<=N;j++)
	{
		if(i!=j and road[i][j])
		{
			mat[j][i]=-1.0/cd[i];
		}
	}
	mat[1][N+1]=1;
}
void work()
{
	int i, j;
	gauss(mat,N);
	for(i=N;i;i--)
	{
		if(fabs(mat[i][i])<eps and fabs(mat[i][N+1])>eps)
		{
			inf[i]=1;
		}
		for(j=i+1;j<=N;j++)if(fabs(mat[i][j])>eps and inf[j])inf[i]=1;
		ans[i]=mat[i][N+1]/mat[i][i];
	}
}
int main()
{
	int q, x, kase=0;
	while(N=read())
	{
		printf("Case #%d:\n",++kase);
		init();
		work();
		q=read();
		while(q--)
		{
			x=read();
			if(inf[x])
			{
				printf("infinity\n");
			}
			else printf("%.3lf\n",fabs(mat[x][N+1])<eps ? 0.0 :mat[x][N+1]/mat[x][x]);
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kernighan-Lin是一种常用于解决图分割问题的算法。该算法最早由Brian W. Kernighan与S. Lin在1970年提出,至今仍被广泛应用于图论和集成电路设计领域。 Kernighan-Lin算法的目标是将一个图划分为两个子图,使得划分后两个子图的连接边数最小。这一目标可以被转化为最小割问题,即将图中的边分为两个集合,使得两个集合之间的边数最小。 算法的执行过程如下: 首先,随机选择一个初始划分,将顶点分为两个集合。 然后,计算划分的初始割,即计算两个集合之间的边数。 接下来,迭代执行以下步骤,直到不能再进行改进为止: 1. 计算每一个节点在当前划分下的边界增益值,即将节点从一个集合移动到另一个集合所能带来的割值的改变。 2. 选择边界增益值最大的节点,并将其从当前集合中移动到另一个集合,更新划分和割值。 3. 重复步骤1和2,直到找不到能够改进的节点。 最终,算法输出最优的划分结果。 Kernighan-Lin算法的优点在于简单且易于理解。它可以在较短的时间内得到较好的近似解,对于大规模的图分割问题具有较高的效率。此外,由于算法只涉及局部改动,因此往往能够得到局部最优解。然而,该算法的缺点也比较明显,它无法保证全局最优解且容易陷入局部最优解。 总之,Kernighan-Lin算法是一种常用的图分割算法,通过迭代地移动节点来优化划分结果。它在实际应用中具有较高的效率和易于实现的优点,但同时也存在一定的局限性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值