[SCOI2008]天平 差分约束

~~~题面~~~

题解:

差分约束学得实在是太烂了,,,,QAQ

这里先记下:

a - b >= x  ---> a >= b + x     ---->        b ---> a = x(b连a,边权为x),      ----> 找最长路, --->f[a][b]对应a - b的最小值,

a - b <=x ---->后面的都反过来就好了

关于这道题:

首先我们可以发现它实际上就是告诉了我们一堆这样的关系:

a > b,

a < b,

a = b,

所以我们应该要想到差分约束,

如果直接连,我们发现连边权都没有,,,,

因此我们要对式子进行转化,

以a > b为例:

a > b ----> a - b > 0 ---> a - b >= 1 ---> a >= b + 1,

这就变成了形如上面的式子,

但是我们发现我们得到了a - b 的最大值和最小值还不够,

于是我们对目标进行转化,

题目要求a + b  ? x + y,其中a + b给定,

那么小于的情况实际上就是a - x < y - b || a - y < x - b,

如果是大于,那直接换符号即可,

如果是等于还要判断a - x的最大最小值相等,因为要确定的结果才能被计入答案,

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 52
  5 /*考虑差分约束中路的长度的意义。
  6 n很小,所以考虑floyd*/
  7 int n,a,b,ans1,ans2,ans3;
  8 int f_min[AC][AC], f_max[AC][AC];
  9 char c[AC];
 10 
 11 inline void upmin(int &a,int b)
 12 {
 13     if(b < a) a = b;
 14 }
 15 
 16 inline void upmax(int &a,int b)
 17 {
 18     if(b > a) a = b;
 19 }
 20 
 21 inline void pre()
 22 {
 23     scanf("%d%d%d",&n,&a,&b);
 24     memset(f_min,63,sizeof(f_min));
 25     for(R i=1;i<=n;i++)
 26     {
 27         scanf("%s",c+1);
 28         for(R j=1;j<=n;j++)
 29         {
 30             if(c[j] == '=' || i == j)
 31                 f_min[i][j] = f_max[i][j] = 0;
 32             else if(c[j] == '+')
 33                 f_min[i][j] = 2, f_max[i][j] = 1;//s[i] - s[j] <= 2, s[i] - s[j] >= 1(最大是3 - 1 = 2)
 34             else if(c[j] == '-')
 35                 f_min[i][j] = -1, f_max[i][j] = -2;//s[i] - s[j] <= -1, s[i] - s[j] >= -2
 36             else 
 37                 f_min[i][j] = 2, f_max[i][j] = -2;//s[i] - s[j] <= 2, i - j >= -2
 38         }
 39     }
 40 
 41 }
 42 #define f_min f_min
 43 #define f_max f_max
 44 void floyd()
 45 {
 46     /*for(R k=1;k<=n;k++)
 47         for(R i=1;i<=n;i++)
 48         {
 49             if(i == k) continue;//防止自己做中转
 50             for(R j=1;j<=n;j++)
 51             {
 52                 if(i == j) continue;//自己到自己是没有用的
 53                 upmin(f_min[i][j], f_min[i][k] + f_min[k][j]);
 54                 upmax(f_max[i][j], f_max[i][k] + f_max[k][j]);
 55             }
 56         }*/
 57     for(int k=1;k<=n;k++)            //Floyd
 58         for(int i=1;i<=n;i++) 
 59         {  
 60             if(i==k) continue;    
 61             for(int j=1;j<=n;j++)
 62             {
 63                 if(i==j) continue;
 64                 f_min[i][j]=min(f_min[i][j],f_min[i][k]+f_min[k][j]);//上界求最短路
 65                 f_max[i][j]=max(f_max[i][k]+f_max[k][j],f_max[i][j]);//下界求最长路 
 66             }
 67         }
 68 }
 69 
 70 void work()
 71 {//error!!!额。。。最长路对应最小值,最短路对应最大值
 72     /*for(R i=1;i<=n;i++)
 73     {
 74         for(R j=1;j<=n;j++)
 75             printf("%d ",f_min[i][j]);
 76         printf("
");
 77     }
 78     printf("
");
 79     for(R i=1;i<=n;i++)
 80     {
 81         for(R j=1;j<=n;j++)
 82             printf("%d ",f_max[i][j]);
 83         printf("
");
 84     }*/
 85     for(R i = 1; i <= n; i++)//强行枚举每一种可能
 86     {
 87         if(i == a || i == b) continue;//不能选已经选定了的
 88         for(R j = 1; j < i; j++)//<i防止重复统计
 89         {
 90             if(j == a || j == b) continue;//同上
 91             if(f_min[a][i] < f_max[j][b] || f_min[a][j] < f_max[i][b]) ++ans3;
 92             if(f_max[a][i] > f_min[j][b] || f_max[a][j] > f_min[i][b]) ++ans1;
 93             if(f_max[a][i] == f_min[a][i] && f_max[j][b] == f_min[j][b] && f_max[a][i] == f_max[j][b]) ++ans2;
 94             else if(f_min[a][j] == f_max[a][j] && f_min[i][b] == f_max[i][b] && f_min[a][j] == f_min[i][b]) ++ans2;
 95         }
 96     }
 97     printf("%d %d %d
",ans1,ans2,ans3);
 98 }
 99 
100 int main()
101 {
102     freopen("in.in","r",stdin);
103     pre();
104     floyd();
105     work();
106     fclose(stdin);
107     return 0;
108 }
原文地址:https://www.cnblogs.com/ww3113306/p/9158377.html