1. 程式人生 > >【洛谷】2474:[SCOI2008]天平【差分約束系統】

【洛谷】2474:[SCOI2008]天平【差分約束系統】

P2474 [SCOI2008]天平

題目背景

2008四川NOI省選

題目描述

你有n個砝碼,均為1克,2克或者3克。你並不清楚每個砝碼的重量,但你知道其中一些砝碼重量的大小關係。你把其中兩個砝碼A 和B 放在天平的左邊,需要另外選出兩個砝碼放在天平的右邊。問:有多少種選法使得天平的左邊重(c1)、一樣重(c2)、右邊重(c3)?(只有結果保證惟一的選法才統計在內)

輸入輸出格式

輸入格式:

 

第一行包含三個正整數n,A,B(1<=A,B<=N,A 和B 不相等)。砝碼編號

為1~N。以下n行包含重量關係矩陣,其中第i行第j個字元為加號“+”表示砝

碼i比砝碼j重,減號“-”表示砝碼i比砝碼j 輕,等號“=”表示砝碼i和砝碼

j一樣重,問號“?”表示二者的關係未知。存在一種情況符合該矩陣。

 

輸出格式:

 

僅一行,包含三個整數,即c1,c2和c3。

 

輸入輸出樣例

輸入樣例#1: 複製
6 2 5
?+????
-?+???
?-????
????+?
???-?+
????-?
輸出樣例#1: 複製
1 4 1
輸入樣例#2: 複製
14 8 4
?+???++?????++
-??=?=???????=
??????????=???
?=??+?==??????
???-???-???-??
-=????????????
-??=???=?-+???
???=+?=???????
??????????????
??????+???????
??=???-????-??
????+?????+???
-?????????????
-=????????????
輸出樣例#2: 複製
18 12 11

說明

4<=n<=50


Solution

題目要求的實際上是滿足$A+B>i+j,A+B=i+j,A+B<i+j$的所有$i,j$組合有多少組。

比如第一個式子,可以轉換為$A-i>j-B$

看上去很差分約束啊!

所以我們可以用$floyed$處理出任意兩點$i-j$的最大值和最小值,初始化賦值顯然。

然後列舉所有的$i,j$組合,判斷是否滿足不等式即可。注意滿足的條件是最小值大於最大值等等。

Code

#include<bits/stdc++.h>
using namespace std;

char s[66];
int dx[55][55], dn[55][55], n, A, B;
int main() {
    scanf("%d%d%d", &n, &A, &B);
    memset(dx, 0x3f3f3f3f, sizeof(dx));
    memset(dn, -0x3f3f3f3f, sizeof(dn));
    for(int i = 1; i <= n; i ++) {
        scanf("%s", s + 1);
        for(int j = 1; j <= strlen(s + 1); j ++) {
            if(s[j] == '=' || i == j) {
                dn[i][j] = dx[i][j] = 0;
            } else if(s[j] == '+') {
                dn[i][j] = 1; dx[i][j] = 2;
            } else if(s[j] == '-') {
                dn[i][j] = -2; dx[i][j] = -1;
            } else {
                dn[i][j] = -2; dx[i][j] = 2;
            }
        }
    }
    
    for(int k = 1; k <= n; k ++)
        for(int i = 1; i <= n; i ++)
            for(int j = 1; j <= n; j ++) {
                dx[i][j] = min(dx[i][j], dx[i][k] + dx[k][j]);
                dn[i][j] = max(dn[i][j], dn[i][k] + dn[k][j]);
            }
    int c1 = 0, c2 = 0, c3 = 0;
    for(int i = 1; i <= n; i ++) {
        if(i == A || i == B)    continue;
        for(int j = 1; j < i; j ++) {
            if(j == A || j == B)    continue;
            if(dn[A][i] > dx[j][B] || dn[B][i] > dx[j][A])
                c1 ++;
            if(dn[i][A] > dx[B][j] || dn[i][B] > dx[A][j])
                c3 ++;
            if((dn[A][i] == dx[A][i] && dn[j][B] == dx[j][B] && dn[A][i] == dn[j][B])
                 || (dn[B][i] == dx[B][i] && dn[j][A] == dx[j][A] && dn[B][i] == dn[j][A]))
                c2 ++;
        }
    }
    printf("%d %d %d", c1, c2, c3);
    return 0;
}