P2294 [HNOI2005]狡猾的商人
题意:
你需要调查某个商人的账本,给你n个月内,m条账单信息,每条账单信息为x到y月的收入或者支出多少钱,问你根据账单信息判断这个账本是否合理
5 3
1 5 100
3 5 50
1 2 51
比如样例:
1到5月收入100,3到5月收入50,1到2月收入51
51 + 50 != 100,账单不合理
题解:
差分约束的题
我们用sun[i]表示前i个月的总利润
如果从第x月到第y月的利润是z(z可正可负)
可以得到:sum[y] - sum[x-1] = z(可以理解为前缀和差值)
相当于我们会得到很多这样的式子,然后去验证是否合法
我们都知道差分约束是小于等于的关系,那这个题怎么做?
sum[y] - sum[x] = z
我们可以认为是同时满足下面两个式子:
sum[y] - sum[x-1] > =val
sum[y] - sum[x-1] < =val第一个式子可以转变成<=的形式:
sum[x-1] - sum[y]< =-val
这样就变成了差分约束的问题
连边(y,x-1),边权为-val
连边(x-1,y),边权为val
然后跑最短路去验证是否有负环…(剩下的都是差分约束的过程了)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+9;
struct node{int v,w;node(int v=0, int w=0):v(v),w(w){};
};
vector<node>edge[maxn];
bool vis[maxn];
int dis[maxn];
int cnt[maxn];
int n,m;
bool spfa(int now)
{queue<int>q;dis[now]=0;q.push(now);vis[now]=1;while(!q.empty()){int u=q.front();q.pop();vis[u]=0;for(int i=0;i<edge[u].size();i++){int v=edge[u][i].v;int w=edge[u][i].w;if(dis[v]>dis[u]+w){dis[v]=dis[u]+w;cnt[v]=cnt[u]+1;if(cnt[v]>=n+1)return 1;if(vis[v]==0){vis[v]=1;q.push(v);}}}}return 0;
}
void init()
{for(int i=0;i<=n;i++)edge[i].clear();memset(dis,0x3f,sizeof(dis));memset(cnt,0,sizeof(cnt));memset(vis,0,sizeof(vis));
}
int main()
{int t;cin>>t;while(t--){cin>>n>>m;init();for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;edge[v].push_back((node){u-1,-w});edge[u-1].push_back((node){v,w});}for(int i=1;i<=n;i++)edge[n+1].push_back((node){i,0});if(spfa(n+1))cout<<"false"<<endl;else cout<<"true"<<endl;}}
/*
2
5 3
1 5 100
3 5 50
1 2 51
3 3
1 2 10
1 3 -5
3 3 -15*/