SCOI 2008 天平 差分约束

SCOI 2008 天平 差分约束

题目链接:SCOI2008天平


​ 首先想到我们要根据给出的重量关系矩阵列出的式子。但是这个矩阵给的只是关系,所以我们列出的只能是不等式组。看到不等式,那么立马联想到这是可以转化一组差分约束系统。但是我们目前知道的信息只有 \(w_i<w_j\),我们得想办法把它转化成形如 \(w_i-w_j<c_k\) 的式子。注意到,题目给出了任意一个砝码的质量 \(w_i\in{1,2,3}\)。所以我们可以得知两个砝码质量相减的最大差值和最小差值,我们设 \(dmx_{i,j},dmi_{i,j}\) 分别表示 \(w_i-w_j\) 可能的最大值和最小值。 同时,由于这题的数据规模并不大,我们可以采取邻接矩阵的方式来存储图。那么初始化程序就显然了,代码如下:

	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			if(s[i][j]=='=') dmx[i][j]=dmi[i][j]=0;
			else if(s[i][j]=='+') dmx[i][j]=2,dmi[i][j]=1;
			else if(s[i][j]=='-') dmx[i][j]=-1,dmi[i][j]=-2;
			else dmx[i][j]=2,dmi[i][j]=-2;
		}
	}
	for(int i=1;i<=n;++i)
		dmx[i][i]=dmi[i][i]=0;

​ 常规的差分约束系统的求解方式多数是建立超级源点求单源最短路得到满足差分约束系统的一组解,但是这里我们注意到,我们要求解的目标不同于其他一般的差分约束的题,其他的差分约束题可能只需要判断有无解就行了,但是这题我们不一样,我们要求出确定关系对的个数。所以单源最短路求出一组解的办法在这题是没有用处的。这也是为什么我们上面对于一条式子 \(c_k\ge w_i-w_j\ge d_k\) 这种式子不建两条边而是用两个邻接矩阵去存储的原因。我们需要拓展差分约束系统中的关系。即,求出每一对 \(i,j\)\(dmx_{i,j},dmi_{i,j}\)。那么扩展的转移式则为:

\[\begin{aligned} dmx_{i,j} &=\min(dmx_{i,k}+dmx_{k,j}) \\ dmi_{i,j} &=\max(dmi_{i,k}+dmi_{k,j}) \end{aligned} \]

​ 为什么 \(dmx\) 的转移要取 \(\min\)\(dmi\) 的转移要取 \(\max\) 呢。因为对于 \(dmx_{i,j}\)\(dmx_{i,j}\) 的值 \(c_k\)。我们可以把它理解成 \(w_i-w_j\le c_k\)。而后面那条式子要理解成:

\[\begin{aligned} w_i-w_k-(w_k-w_j)\le c_u-c_v \\ w_i-w_j\le c_u-c_v \end{aligned} \]

​ 说明 \(w_i-w_j\) 是同时满足小于等于 \(c_k,c_u-c_v\) 的。所以取两者较小值。同理对于 \(dmi\) 要取较大值。

​ 再回到上面的转移式。我们可以注意到这条转移式有点类似于最短/长路的求解。也就是我们绕了一圈子又回到了原点。但是这次我们要求的,是全源最短路。注意到 \(n\) 的范围并不大,所以用 Floyd 来求解这个问题。

​ 最后我们要做的,就是根据已知信息求解答案。由于我们上述式子求出的是 \(w_i-w_j\) 的关系,所以我们判定答案也要围绕这一形式的式子。对于左边比右边重的情况就是 \(w_A-w_i+w_B-w_j>0\)。由于我们要保证结果唯一,所以我们得保证这条式子对于我们求出的关系恒成立。所以我们可以用 \(dmi_{A,i},dmi_{B,i}\) 来判定。同理的,对于右边比左边重的情况就是用 \(dmx_{A,i},dmx{B,i}\) 来判定。相等的情况,因为我们要保证相等,就是 \(dmx_{A,i}=dmx_{B,j}\)\(dmi_{A,i}=dmx_{B,j}\)\(dmi_{A,i}+dmi_{B,j}=0\)

​ 统计部分代码如下:

	for(int i=1;i<=n;++i)
	{
		for(int j=i+1;j<=n;++j)
		{
			if(i==A||i==B||j==A||j==B) continue;
			if(dmi[A][i]+dmi[B][j]>0||dmi[A][j]+dmi[B][i]>0) ++c1;
			if(dmx[A][i]+dmx[B][j]<0||dmx[A][j]+dmx[B][i]<0) ++c3;
			if(dmi[A][i]==dmx[A][i]&&dmi[j][B]==dmx[j][B]&&dmi[A][i]+dmi[B][j]==0) ++c2;
			else if(dmi[A][j]==dmx[A][j]&&dmi[i][B]==dmx[i][B]&&dmi[A][j]+dmi[B][i]==0) ++c2;
		}
	}

​ 此题是差分约束好题,同时也告诉我们差分约束系统和最短路之间暧昧千丝万缕的关系。

全部代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN =55;
int n,A,B;
int dmx[MAXN][MAXN],dmi[MAXN][MAXN];
char s[MAXN][MAXN];
int main()
{
	scanf("%d %d %d",&n,&A,&B);
	for(int i=1;i<=n;++i)
		scanf("%s",s[i]+1);
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=n;++j)
		{
			if(s[i][j]=='=') dmx[i][j]=dmi[i][j]=0;
			else if(s[i][j]=='+') dmx[i][j]=2,dmi[i][j]=1;
			else if(s[i][j]=='-') dmx[i][j]=-1,dmi[i][j]=-2;
			else dmx[i][j]=2,dmi[i][j]=-2;
		}
	}
	for(int i=1;i<=n;++i)
		dmx[i][i]=dmi[i][i]=0;
	for(int k=1;k<=n;++k)
		for(int i=1;i<=n;++i)
			for(int j=1;j<=n;++j)
			{
				dmx[i][j]=min(dmx[i][k]+dmx[k][j],dmx[i][j]);
				dmi[i][j]=max(dmi[i][k]+dmi[k][j],dmi[i][j]);
			}
	int c1=0,c2=0,c3=0;
	for(int i=1;i<=n;++i)
	{
		for(int j=i+1;j<=n;++j)
		{
			if(i==A||i==B||j==A||j==B) continue;
			if(dmi[A][i]+dmi[B][j]>0||dmi[A][j]+dmi[B][i]>0) ++c1;
			if(dmx[A][i]+dmx[B][j]<0||dmx[A][j]+dmx[B][i]<0) ++c3;
			if(dmi[A][i]==dmx[A][i]&&dmi[j][B]==dmx[j][B]&&dmi[A][i]+dmi[B][j]==0) ++c2;
			else if(dmi[A][j]==dmx[A][j]&&dmi[i][B]==dmx[i][B]&&dmi[A][j]+dmi[B][i]==0) ++c2;
		}
	}
	printf("%d %d %d\n",c1,c2,c3);
	return 0;
}

​ 总结:看到不等式就要想到差分约束,看到差分约束就要想到最短路。

posted @ 2021-08-24 10:39  夜空之星  阅读(43)  评论(0编辑  收藏  举报