bzoj1077: [SCOI2008]天平 差分约束
Description
你有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。
题解
由于题目要求求\(A+B\)与\(i+j\)的关系,我们考虑移位,考虑\(A-i\)和\(j-B\)的关系,记\(mx[i][j]\)和\(mn[i][j]\)为\(i-j\)的最大值和最小值。然后根据题目描述确定\(i,j\)关系,接着\(Floyd\)跑最短路,基本的差分约束操作,然后找到一定符合要求的更新答案。详细的看代码~
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,A,B;
char s[60];
int mx[60][60],mn[60][60];
int c1,c2,c3;
int main(){
ios::sync_with_stdio(false);
cin>>n>>A>>B;
for(int i=1;i<=n;i++){
cin>>(s+1);
for(int j=1;j<=n;j++){
if(s[j]=='='||i==j) mx[i][j]=mn[i][j]=0;
else if(s[j]=='+') mx[i][j]=2,mn[i][j]=1;//若i>j则i-j<=2 i-j>=1
else if(s[j]=='-') mx[i][j]=-1,mn[i][j]=-2;//若i<j 则i-j<=1 i-j>=-2
else mx[i][j]=2,mn[i][j]=-2;
}
}
for(int k=1;k<=n;k++) for(int i=1;i<=n;i++){ //Floyd求最短路
if(i==k)continue;
for(int j=1;j<=n;j++){
if(i==j||i==k)continue;
mx[i][j]=min(mx[i][j],mx[i][k]+mx[k][j]);
mn[i][j]=max(mn[i][j],mn[i][k]+mn[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||j==i)continue;
if(mn[A][i]>mx[j][B]||mn[B][i]>mx[j][A])c1++; //若A+B>i+j 则A-i的最小值一定大于j-B的最大值
if(mn[i][A]>mx[B][j]||mn[i][B]>mx[A][j])c3++;//若A+B<i+j 则i-A的最小值一定大于B-j的最大值
if((mn[A][i]==mx[A][i]&&mn[j][B]==mx[j][B]&&mn[A][i]==mn[j][B])||(mn[A][j]==mx[A][j]&&mn[i][B]==mx[i][B]&&mn[A][j]==mn[i][B]))c2++;//必须全部相等
}
}
cout<<c1<<" "<<c2<<" "<<c3<<endl;
return 0;
}