#差分约束,Floyd#洛谷 2474 [SCOI2008]天平
分析
非传统差分约束??
注意只有结果保证惟一的选法才统计在内
这就为差分约束提供了依据
以左边重为例,假设现在选择的砝码为\(i,j\),
那么\(\because A+B>i+j\therefore A-i>j-B\)
既然结果保证唯一,那么也就是\(min(A-i)>max(j-B)\)
右边重同理,也就是\(max(A-i)<min(j-B)\)
同样重也很显然,就是
\(min(A-i)=max(A-i),min(j-B)=max(j-B),max(A-i)=min(j-B)\)
讲到这里该怎么做,floyd现身了,考虑维护最短路和最长路,
- 等号也就是\(i-j\leq 0,i-j\geq 0\)
- 大于号也就是\(i-j\leq -1,i-j\geq -2\)
- 小于号也就是\(i-j\leq 2,i-j\geq 1\)
- 问号也就是\(i-j\geq -2,i-j\leq 2\)
按照这个floyd建图分别跑最短路和最长路即可
这和传统的SPFA有什么区别呢,可以发现它求的是多源最短路,
那么Floyd要比SPFA更加方便
代码
#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=51; char s[N];
int n,A,B,dmn[N][N],dmx[N][N],ans1,ans2,ans3;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed min(int a,int b){return a<b?a:b;}
inline signed max(int a,int b){return a>b?a:b;}
signed main(){
scanf("%d%d%d",&n,&A,&B);
for (rr int i=1;i<=n;++i){
scanf("%s",s+1),s[i]='=';
for (rr int j=1;j<=n;++j)
switch (s[j]){
case '=':{
dmn[i][j]=0,dmx[i][j]=0;
break;
}
case '+':{
dmn[i][j]=1,dmx[i][j]=2;
break;
}
case '-':{
dmn[i][j]=-2,dmx[i][j]=-1;
break;
}
case '?':{
dmn[i][j]=-2,dmx[i][j]=2;
break;
}
}
}
for (rr int k=1;k<=n;++k)
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<=n;++j)
if ((i^k)&&(i^j)&&(k^j))
dmn[i][j]=max(dmn[i][j],dmn[i][k]+dmn[k][j]),//下界
dmx[i][j]=min(dmx[i][j],dmx[i][k]+dmx[k][j]);//上界
for (rr int i=1;i<=n;++i)
for (rr int j=1;j<i;++j)
if ((i^j)&&(i^A)&&(i^B)&&(j^A)&&(j^B)){
if (dmn[A][i]>dmx[j][B]||dmn[B][i]>dmx[j][A]) ++ans1;
if (dmn[i][A]>dmx[B][j]||dmn[i][B]>dmx[A][j]) ++ans3;
if ((dmn[A][i]==dmx[A][i]&&dmn[j][B]==dmx[j][B]&&dmn[A][i]==dmx[j][B])||
(dmn[A][j]==dmx[A][j]&&dmn[i][B]==dmx[i][B]&&dmn[A][j]==dmx[i][B]))
++ans2;
}
return !printf("%d %d %d",ans1,ans2,ans3);
}