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;
}
posted @ 2018-10-08 00:03  南城ㄱ  阅读(214)  评论(0编辑  收藏  举报