【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。
Sample Input
6 2 5
?+????
-?+???
?-????
????+?
???-?+
????-?
Sample Output
1 4 1
HINT
【数据规模】 4<=n<=50
题解
比较巧妙的差分约束的题,题目让求A+B>|<|=C+D的个数并且让是唯一确定的,所以所以拿A+B>C+D来说,移项得A-C>B-D或A-D>C-B,所以就如果有解,就必须是A-C的最小值>B-D的最大值,或A-D的最小值>C-B的最大值,那么我们就用dmin[i][j]记录j-i差的最小值,用dmax[i][j]记录j-i的最大值,为什么都是j-i呢,因为连边的条件与表示的差是反着的,这样做两遍Floyd就行了,剩下的就是分类讨论了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=60;
const int inf=10000000; 
int n,A,B,dmin[N][N],dmax[N][N],ans1,ans2,ans3;
char a[N][N];
int main(){
    scanf("%d%d%d",&n,&A,&B);
    for(int i=0;i<=n+1;i++) for(int j=0;j<=n+1;j++) if(i!=j) dmin[i][j]=inf;
    for(int i=1;i<=n;i++) dmin[n+1][i]=dmin[i][0]=0;
    dmin[0][n+1]=2,dmin[n+1][0]=-2;
    for(int i=1;i<=n;i++){
        scanf("%s",a[i]+1);
        for(int j=1;j<=n;j++){
            if(a[i][j]=='-') dmin[j][i]=-1;
            if(a[i][j]=='=') dmin[i][j]=0,dmin[j][i]=0;
        }
    }
    for(int k=0;k<=n+1;k++) for(int i=0;i<=n+1;i++) for(int j=0;j<=n+1;j++)
    if(k!=i&&k!=j) dmin[i][j]=min(dmin[i][j],dmin[i][k]+dmin[k][j]);
     
    for(int i=0;i<=n+1;i++) for(int j=0;j<=n+1;j++) if(i!=j) dmax[i][j]=-inf;
    for(int i=1;i<=n;i++) dmax[i][n+1]=dmax[0][i]=0;
    dmax[n+1][0]=-2,dmax[0][n+1]=2;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(a[i][j]=='-') dmax[i][j]=1;
            if(a[i][j]=='=') dmax[i][j]=0,dmax[j][i]=0;
        }
    }
    for(int k=0;k<=n+1;k++) for(int i=0;i<=n+1;i++) for(int j=0;j<=n+1;j++) 
    if(k!=i&&k!=j) dmax[i][j]=max(dmax[i][j],dmax[i][k]+dmax[k][j]);
    for(int C=1;C<=n;C++){
        if(C!=A&&C!=B){
            for(int D=1;D<C;D++){
                if(D!=A&&D!=B){
                    if(dmin[A][C]<dmax[D][B]||dmin[A][D]<dmax[C][B]) ans1++;
                    if((dmin[C][A]==dmax[C][A]&&dmin[B][D]==dmax[B][D]&&dmin[C][A]==dmax[B][D])||(dmin[D][A]==dmax[D][A]&&dmin[B][C]==dmax[B][C]&&dmin[D][A]==dmax[B][C]))
                    ans2++;
                    if(dmax[A][C]>dmin[D][B]||dmax[A][D]>dmin[C][B]) ans3++; 
                }
            }
        }
    }
    printf("%d %d %d\n",ans1,ans2,ans3);
    return 0;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章