题目描述
题目大意:有n个砝码,重量只可能为1g,2g或3g,给出一些砝码之间的重量关系。现在将a和b两个砝码放在天平的左边,现在要选出另外两个砝码放在方程的右边,问有多少种方法使得天平左边重、一样重、右边重。(只有结果保证惟一的选法才统计在内)
题解
对于两个砝码x,y,若只考虑+和=的情况,那么可以列出不等式
d(x)-d(y)>=1;d(x)>=d(y),d(y)>=d(x)
对于最小值设一个源点d(0),满足d(0)=3,且d(i)<=d(0);对于最大值设d(0),满足d(0)=1,且d(i)>=d(0)
这样搞成差分约束最短路的模型,跑单源最短路和单源最长路求出每一个点可能的最大值和最小值
然后对于任意的两个点xy,在图中暴力找出xy的关系,即x>y,y>x,x=y或xy不相关
暴力枚举放在右边的两个砝码,再暴力枚举这4个砝码的重量[Min,Max],然后暴力判断这4个砝码的重量关系是否满足在图中的关系,同时满足一种情况才合法
刚开始的时候让我困惑的一点是在暴力枚举重量之后,为什么只需要判断这4个砝码的关系就行了,不需要考虑其它的砝码。实际上这个问题非常蠢,题目保证了至少有一种可行解,在[Min,Max]区间内枚举就保证了其它的点都有可行解
代码
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
#define N 55
#define E 10005
int n,a,b,inf,c1,c2,c3;
char s[N];bool flag[N],can[N][N];
int tot,point[N],nxt[E],v[E],c[E],_tot,_point[N],_nxt[E],_v[E],_c[E];
int dis[N],_dis[N],d[N][N];bool vis[N];
queue <int> q;
void add(int x,int y,int z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
}
void _add(int x,int y,int z)
{
++_tot; _nxt[_tot]=_point[x]; _point[x]=_tot; _v[_tot]=y; _c[_tot]=z;
}
void spfa()
{
memset(dis,127,sizeof(dis));
dis[0]=3;vis[0]=1;q.push(0);
while (!q.empty())
{
int now=q.front();q.pop();
vis[now]=0;
for (int i=point[now];i;i=nxt[i])
if (dis[v[i]]>dis[now]+c[i])
{
dis[v[i]]=dis[now]+c[i];
if (!vis[v[i]]) vis[v[i]]=1,q.push(v[i]);
}
}
}
void _spfa()
{
memset(_dis,128,sizeof(_dis));
_dis[0]=1;vis[0]=1;q.push(0);
while (!q.empty())
{
int now=q.front();q.pop();
vis[now]=0;
for (int i=_point[now];i;i=_nxt[i])
if (_dis[_v[i]]<_dis[now]+_c[i])
{
_dis[_v[i]]=_dis[now]+_c[i];
if (!vis[_v[i]]) vis[_v[i]]=1,q.push(_v[i]);
}
}
}
void dfs(int x,int rt)
{
vis[x]=1;can[rt][x]=1;
for (int i=point[x];i;i=nxt[i])
if (!vis[v[i]])
dfs(v[i],rt);
}
bool check(int A,int B,int I,int J,int a,int b,int i,int j)
{
if (d[A][B]!=inf)
{
if (a<b&&d[A][B]>=0) return 0;
if (a==b&&d[A][B]) return 0;
if (a>b&&d[A][B]<=0) return 0;
}
if (d[A][I]!=inf)
{
if (a<i&&d[A][I]>=0) return 0;
if (a==i&&d[A][I]) return 0;
if (a>i&&d[A][I]<=0) return 0;
}
if (d[A][J]!=inf)
{
if (a<j&&d[A][J]>=0) return 0;
if (a==j&&d[A][J]) return 0;
if (a>j&&d[A][J]<=0) return 0;
}
if (d[B][I]!=inf)
{
if (b<i&&d[B][I]>=0) return 0;
if (b==i&&d[B][I]) return 0;
if (b>i&&d[B][I]<=0) return 0;
}
if (d[B][J]!=inf)
{
if (b<j&&d[B][J]>=0) return 0;
if (b==j&&d[B][J]) return 0;
if (b>j&&d[B][J]<=0) return 0;
}
if (d[I][J]!=inf)
{
if (i<j&&d[I][J]>=0) return 0;
if (i==j&&d[I][J]) return 0;
if (i>j&&d[I][J]<=0) return 0;
}
return 1;
}
int compare(int A,int B,int I,int J)
{
int c1=0,c2=0,c3=0;
for (int a=_dis[A];a<=dis[A];++a)
for (int b=_dis[B];b<=dis[B];++b)
for (int i=_dis[I];i<=dis[I];++i)
for (int j=_dis[J];j<=dis[J];++j)
if (check(A,B,I,J,a,b,i,j))
{
if (a+b>i+j) ++c1;
if (a+b==i+j) ++c2;
if (a+b<i+j) ++c3;
}
if (c1&&!c2&&!c3) return 1;
if (!c1&&c2&&!c3) return 2;
if (!c1&&!c2&&c3) return 3;
return 0;
}
int main()
{
scanf("%d%d%d",&n,&a,&b);
for (int i=1;i<=n;++i)
{
scanf("%s",s+1);
for (int j=1;j<=n;++j)
if (s[j]=='+')
add(i,j,-1),_add(j,i,1);
else if (s[j]=='='&&i<=j)
add(i,j,0),add(j,i,0),_add(i,j,0),_add(j,i,0);
}
for (int i=1;i<=n;++i) add(0,i,0),_add(0,i,0);
spfa();_spfa();
for (int i=1;i<=n;++i)
{
memset(vis,0,sizeof(vis));
dfs(i,i);
}
memset(d,127,sizeof(d));inf=d[0][0];
for (int i=1;i<=n;++i)
for (int j=i;j<=n;++j)
{
if (can[i][j]&&can[j][i]) d[i][j]=d[j][i]=0;
else if (can[i][j]) d[i][j]=1,d[j][i]=-1;
else if (can[j][i]) d[i][j]=-1,d[j][i]=1;
}
for (int i=1;i<=n;++i)
if (i!=a&&i!=b)
for (int j=i+1;j<=n;++j)
if (j!=a&&j!=b)
{
int opt=compare(a,b,i,j);
if (opt==1) ++c1;
if (opt==2) ++c2;
if (opt==3) ++c3;
}
printf("%d %d %d\n",c1,c2,c3);
}