【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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章