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 }
非做顽石不可,哪管他敬仰暗唾