【题解】 [HNOI2005]狡猾的商人(差分约束)

题面懒得复制,戳我戳我

Solution:

  • 其实这个差分是挺显然的,我们可以用(s[i])表示从第(1)(i)中间的收入和
  • 重点就在式子,比如读入(a),(b),(c),显然可以得到一个式子:$$s[b]-s[a-1]==c$$把这个式子变成不等式就是$$s[b]>=c+s[a-1]$$$$s[b]>=c+s[a-1]$$第二个式子又可以转换成$$s[a-1]<=-c+s[b]$$这就显然是从(a-1)连向(b)一条长度为(c)的边,从(b)连向(a-1)一条(-c)的边
  • 然后我们就可以跑(SPFA)了,这有一点就是,因为要满足所有条件,每天的收入是固定的,我们就只用如下操作:更新至的点如果为(INF)(初值),就更新,否则判断是否符合边的条件要求(s[v]==s[u]+dis[u,v])(u为出发点,v为到达点)
  • 我傻逼的没有清空(vis)数组,WA了无数次(太愚蠢了)

BTW:这题还可以用并查集,可以去思考丢个链接吧,Awson太强辣


Code:

//It is coded by Ning_Mew on 3.29
#include<bits/stdc++.h>
using namespace std;

const int maxn=1000+10;

int T,n,m,INF;
int head[maxn],cnt=0,dist[maxn];
bool be[maxn];
struct Edge{int nxt,to,dis;}edge[maxn*2];

void add(int from,int to,int dis){
  edge[++cnt].nxt=head[from];
  edge[cnt].to=to;
  edge[cnt].dis=dis;
  head[from]=cnt;
}
int vis[maxn];
bool SPFA(int k,int ls){
  queue<int>Q;while(!Q.empty())Q.pop();
  vis[k]=ls;Q.push(k);be[k]=false;
  dist[k]=0;
  while(!Q.empty()){
    int u=Q.front();Q.pop();vis[u]=ls-1;
    for(int i=head[u];i!=0;i=edge[i].nxt){
      int v=edge[i].to;be[v]=false;
      if(dist[v]==INF){
        dist[v]=dist[u]+edge[i].dis;
        if(vis[v]!=ls){
          Q.push(v);
          vis[v]=ls;
        }
      }
      else{
   		if(dist[v]!=dist[u]+edge[i].dis)return false;
      }
    }
  }return true;
}
void work(){
  scanf("%d%d",&n,&m);
  memset(head,0,sizeof(head));
  memset(be,false,sizeof(be));cnt=0;
  memset(vis,0,sizeof(vis));
  for(int i=1;i<=m;i++){
    int a,b,c;scanf("%d%d%d",&a,&b,&c);
    add(a-1,b,c);be[a-1]=true;be[b]=true;
    	add(b,a-1,-1*c);
  }
  memset(dist,-0x5f,sizeof(dist));
  INF=dist[0];
  int ls=1;
  for(int i=0;i<=n;i++){
    if(be[i]==false)continue;
    //cout<<i<<endl;
    if(SPFA(i,++ls));else{
      printf("false
");
      return;
    }
  }printf("true
");
}
int main(){
  scanf("%d",&T);
  for(int i=1;i<=T;i++){work();}  
  return 0;
}