差分约束入门:[bzoj]1077: [SCOI2008]天平

近来重新拾起了早已忘记的差分约束,做了这样一道入门题:

题面
你有n个砝码,均为1克,2克或者3克。你并不清楚每个砝码的重量,但你知道其中一些砝码重量的大小关系。你把其中两个砝码A和B放在天平的左边,需要另外选出两个砝码放在天平的右边。问:有多少种选法使得天平的左边重(c1)、一样重(c2)、右边重(c3)?(只有结果保证惟一的选法才统计在内)

input:
第一行包含三个正整数n,A,B(1<=A,B<=N,A和B不相等)。砝码编号为1~N。以下n行包含重量关系矩阵,其中第i行第j个字符为加号“+”表示砝码i比砝码j重,减号“-”表示砝码i比砝码j轻,等号“=”表示砝码i和砝码j一样重,问号“?”表示二者的关系未知。存在一种情况符合该矩阵

output
仅一行,包含三个整数,即c1,c2和c3。

样例与数据范围
input:
6 2 5
?+???
-?+???
?-???
???+?
???-?+
???-?
outout:
1 4 1
【数据规模】 4<=n<=50

这样的一道题,首先我们看到这些不等式关系很容易就会联想到差分约束系统,那么我就说一下这道题我所建立的不等式:
我用mn[i][j]表示a[i]-a[j]的最小范围,用mx[i][j]表示a[i]-a[j]的最大范围,则有:
1
a[a]+a[b]>a[i]+a[j] ---->
a[a]-a[i]>a[j]-a[b]或a[a]-a[j]>a[i]-a[b] ---->
mn[a][i]>mx[j][b]或mn[a][j]>mx[i][b]
2
a[a]+a[b]=a[i]+a[j] ---->
a[a]-a[i]=a[j]-a[b]或a[a]-a[j]=a[i]-a[b] ---->
mn[a][i]=mx[j][b]或mn[a][j]=mx[i][b]
3
a[a]+a[b]<a[i]+a[j] ---->
a[a]-a[i]<a[j]-a[b]或a[a]-a[j]<a[i]-a[b] ---->
mn[a][i]<mx[j][b]或mn[a][j]<mx[i][b]

第一步我们建出图,对于这道题,我们可以将读入的情况进行以下处理:

1

读入等号代表i与j一样大,我们建立:
mn[i][j]=0;
mx[i][j]=0;

2

读入小于号代表i比j小,我们建立:
mn[i][j]=-2;
mx[i][j]=-1;

3

读入大于号代表i比j小大,我们建立:
mn[i][j]=1;
mx[i][j]=2;

4

读入问号代表i与j关系未知,我们建立:
mn[i][j]=-2
mx[i][j]=2

最后我们建立的图就是这个样子的:
(红色代表两条?边,为了使图更加清晰就统一将?的两条边用红色表示了)

然后使用floyd对这个图求任意i到j的最短路,跑完之后图就变成了这个样子:
(每条颜色的箭头方向边都存在一条与之反向且数值为相反数的边,为了保证图的清晰也略去了)
在这里插入图片描述
跑完了任意点之间的大小关系,接着就需要判断

左边重(c1)、一样重(c2)、右边重(c3)

我们使用文章上面提出的不等式:

mn[a][i]>mx[j][b]或mn[a][j]>mx[i][b]
mn[a][i]=mx[j][b]或mn[a][j]=mx[i][b]
mn[a][i]<mx[j][b]或mn[a][j]<mx[i][b]

注意等于的情况还要判断mx与mn是否相等。

判断后就可以得出所求的c1,c2,c3了。最后贴上代码:

#include<bits/stdc++.h>
#define maxn 55
using namespace std;
int mx[maxn][maxn],mn[maxn][maxn],a,b,n;
int c1,c2,c3;
char c[maxn];
int main()
{
	scanf("%d%d%d",&n,&a,&b);
	for(int i=1;i<=n;i++){
		cin>>c;
		for(int j=0;c[j]!='\000';j++){
			if(c[j]=='-'){mn[i][j+1]=-2,mx[i][j+1]=-1;continue;}
			if(c[j]=='='){mn[i][j+1]=0,mx[i][j+1]=0;continue;}
			if(c[j]=='+'){mn[i][j+1]=1,mx[i][j+1]=2;continue;}
			mn[i][j+1]=-2,mx[i][j+1]=2;
		}
		mn[i][i]=0;
		mx[i][i]=0;
	}
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if((i!=j&&i!=k&&j!=k)){
					mn[i][j]=max(mn[i][j],mn[i][k]+mn[k][j]);
					mx[i][j]=min(mx[i][j],mx[i][k]+mx[k][j]);
				}
	for(int i=1;i<=n;i++){
		if(i==a||i==b) continue;
		for(int j=i+1;j<=n;j++){
			if(j==a||j==b) continue;
			if(mn[a][i]>mx[j][b]||mn[a][j]>mx[i][b]) c1++;
            if((mn[a][i]==mx[a][i]&&mn[b][j]==mx[b][j]&&mn[a][i]==mx[j][b])||(mn[a][j]==mx[a][j]&&mn[b][i]==mx[b][i]&&mn[a][j]==mx[i][b])) c2++;
            if(mx[a][i]<mn[j][b]||mx[a][j]<mn[i][b]) c3++;
		}
	}
	cout<<c1<<" "<<c2<<" "<<c3;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值