luogu 2474 [SCOI2008]天平

输入输出样例

输入 #1
6 2 5
?+????
-?+???
?-????
????+?
???-?+
????-?
输出 #1
1 4 1
输入 #2
14 8 4
?+???++?????++
-??=?=???????=
??????????=???
?=??+?==??????
???-???-???-??
-=????????????
-??=???=?-+???
???=+?=???????
??????????????
??????+???????
??=???-????-??
????+?????+???
-?????????????
-=????????????
输出 #2
18 12 11

说明/提示

4<=n<=50

 

分析

Part 1 建图

这道题,点与点之间大小相对,变量之间有约束条件

还是差分约束最好了

怎么建图,他只给了大小关系,并没有说具体数值,难道直接连一条长度为1 或-1的边?

再看题目所求:

你把其中两个砝码A 和B 放在天平的左边,需要另外选出两个砝码放在天平的右边。问:有多少种选法使得天平的左边重(c1)、一样重(c2)、右边重(c3)?(只有结果保证惟一的选法才统计在内)

明显求的是一个大小关系

我们在读题,题目上说“存在一种情况符合该矩阵”,所以可能不止一种

这样,我们便不能直接将距离赋值为1了,而是需要保留这个范围

留意到数据范围,结合可以“选任意两个砝码”,用矩阵存图会比较方便

既然我们要求的是一个范围,连边的时候,连上下界即可

用一个数组存最大的差,一个数组存最小的差

Part 2 求解 

想要求得这个差分约束系统的解,我们的目的是求出各个砝码的取值范围

由于砝码自由组合,还是用Floyd比较好

这样求出来的是什么?是两个砝码之间的差的取值范围

要用这个推出各个砝码重量的取值范围吗?

不必,也不行

就算推出来了,有些砝码的有些取值是不能同时取到的

我们看题,所求的是:

i+j<a+b的个数(i-a<b-j)(j-a<b-i)

i+j>a+b的个数(i-a>b-j)(j-a>b-i)

i+j=a+b的个数(i-a=b-j)(i-b=a-j)

这样,就可以将和转化为差求解了

再看题:

只有结果保证惟一的选法才统计在内

这是一个恒成立问题,所以要最大的可能小于最小的可能才叫恒成立

等于的时候,还需要最大值等于最小值

 

完结撒花!

代码

  1 /************************
  2 User:Mandy.H.Y
  3 Language:c++
  4 Problem:luogu2474
  5 Algorithm:
  6 ************************/
  7 
  8 #include<bits/stdc++.h>
  9 
 10 using namespace std;
 11 
 12 const int maxn = 55;
 13 
 14 int n,a,b; 
 15 char c[maxn][maxn]; 
 16 int dis[maxn][maxn],dis1[maxn][maxn];
 17 
 18 template<class T>inline void read(T&x){
 19     x = 0;bool flag = 0;char ch = getchar();
 20     while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
 21     while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
 22     if(flag) x = -x;
 23 }
 24 
 25 template<class T>void putch(const T x){
 26     if(x > 9) putch(x / 10);
 27     putchar(x % 10 | 48);
 28 }
 29 
 30 template<class T>void put(const T x){
 31     if(x < 0) putchar('-'),putch(-x);
 32     else putch(x);
 33 }
 34 
 35 void file(){
 36     freopen("2474.in","r",stdin);
 37 //    freopen("2474.out","w",stdout);
 38 }
 39 
 40 void readdata(){
 41     read(n);read(a);read(b);
 42     for(int i = 0;i < n; ++ i) scanf("%s",c[i]);
 43     for(int i = 0;i < n; ++ i) c[i][i] = '=';
 44 }
 45 
 46 void work(){ 
 47 
 48 
 49     for(int i = 1;i <= n; ++ i){ 
 50         for(int j = 1;j <= n; ++ j){
 51             switch(c[i-1][j-1]){
 52                 case '-':{
 53                     dis[i][j] = 1; dis1[i][j] = 2;
 54                     dis[j][i] = -2; dis1[j][i] = -1;
 55                     break;//    dis是最小值,dis1最大值 
 56                 }//虽然样例是对称的,但题目上没说,还是连双向 
 57                     dis[j][i] = 1;dis1[j][i] = 2;
 58                     dis[i][j] = -2;dis1[i][j] = -1;
 59                     break;
 60                 }
 61                 case '=':{
 62                     dis[i][j] = 0;dis[j][i] = 0;
 63                     dis1[j][i] = 0;dis1[i][j] = 0;
 64                     break;
 65                 }
 66                 default:{
 67                     dis[i][j] = -2;dis1[i][j] = 2;
 68                     dis[j][i] = -2;dis1[j][i] = 2;
 69                     break;
 70                 }//?的,赋为最大/小的值 ,可以更新 
 71             }
 72         }
 73     }
 74 //    以最大的可能距离求最短路,便是他们之间的最大差
 75 //    因为最大差就是“最多,大了多少”,它的限制便是最少的“最多” 
 76     for(int k = 1;k <= n; ++ k)
 77         for(int i = 1;i <= n; ++ i)
 78             for(int j = 1;j <= n; ++ j){
 79                 if(i == j || i == k || j == k) continue;
 80                 //初值的关系,最好判断一下 
 81                 dis[i][j] = max(dis[i][j],dis[i][k] + dis[k][j]);
 82                 dis1[i][j] = min(dis1[i][j],dis1[i][k] + dis1[k][j]);
 83             }
 84     int c1 = 0,c2 = 0,c3 = 0;
 85     for(int i = 1;i <= n; ++ i){
 86         if(i == a || i == b) continue;
 87         for(int j = 1+i;j <= n; ++ j){
 88             if(j == a || j == b) continue;
 89             c1 += (dis[i][a] > dis1[b][j] || dis[i][b] > dis1[a][j]);
 90             
 91             c3 += (dis[a][i] > dis1[j][b] || dis[b][i] > dis1[j][a]);
 92             
 93             c2 += ((dis[i][a] == dis1[b][j] && dis[i][a] == dis1[i][a] && dis[b][j] == dis1[b][j]) || 
 94                    (dis[i][b] == dis1[a][j] && dis[i][b] == dis1[i][b] && dis[a][j] == dis1[a][j]));
 95         }
 96     }        
 97             
 98     put(c1);putchar(' ');
 99     put(c2);putchar(' ');
100     put(c3);
101 }
102 
103 int main(){
104 //    file();
105     readdata();
106     work();
107     return 0;
108 }
View Code

 

posted @ 2019-09-09 09:45  Mandy_H_Y  阅读(161)  评论(0编辑  收藏  举报