洛谷P1993:
题意:
小K在MC里面建立很多很多的农场,总共n个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共m个),以下列三种形式描述:
- 农场a比农场b至少多种植了c个单位的作物,
- 农场a比农场b至多多种植了c个单位的作物,
- 农场a与农场b种植的作物数一样多。
但是,由于小K的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。
思路:画了下划线的三句话提示我们这题要用差分约束系统
记val[a]表示第a个农场中了几枝花,从a到b连一条权值为c的边表示
,则若某行输入为1 a b c,则加入边
若某行输入为2 a b c,则加入边
和若某行输入为3 a b,则加入边
和建图成功后,用spfa跑一遍最长路,若有正环,输出No,否则输出Yes
提示:全图不一定强联通,所以每个强联通分量都要跑一遍最长路,若每个强联通分量存在最长路,就输出No
#include <bits/stdc++.h> using namespace std; const int N=10100; struct node{ int next,to,w; }e[N<<1];int h[N],tot; inline void add(int a,int b,int c){ e[++tot]=(node){h[a],b,c};h[a]=tot; } int d[N];bool v[N],vis[N]; //d表示最长路的长度,v和vis表示是否访问过 bool spfa(int u){ vis[u]=v[u]=true;register int i; for(i=h[u];i;i=e[i].next){ register int to=e[i].to; if (d[u]+e[i].w>d[to]){ //别把>写成<了 if (v[to]) return true; else{ d[to]=d[u]+e[i].w; if (spfa(to)) return true; } } } v[u]=false;return false; //千万别修改vis!!! } //用dfs版spfa判断有无正环 int n,m,opt,a,b,c,i; int main(){ freopen("t1.in","r",stdin); memset(d,128,sizeof(d)); scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d%d",&opt,&a,&b); switch(opt){ case 1:scanf("%d",&c);add(a,b,c);break; case 2:scanf("%d",&c);add(b,a,-c);add(a,b,0);break; default:add(a,b,0);add(b,a,0);break; //用switch-case的童鞋,别忘了break } } for(i=1;i<=n;i++) if (!vis[i]){ d[i]=0; if (spfa(i)){ printf("No"); return 0; } } printf("Yes"); return 0; }
洛谷P1726:
题意:在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为<A,B>。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
思路:题意就是让我们找出含顶点个数最多的强联通分量,既然是找强联通分量,肯定要用tarjan算法了,在处理每个点属于哪个强联通分量时,顺便求出每个强联通分量有多少个顶点在内,最后结合所有信息输出即可。
#include <bits/stdc++.h> using namespace std; const int N=5010; const int M=50100; struct node{ int next,to; }e[M<<1];int h[N],tot; //注意e要开到M*2,而不是M inline void add(int a,int b){ e[++tot]=(node){h[a],b};h[a]=tot; } int dfn[N],low[N],dfscnt; int Stack[N],stack_top; int belong[N],col,num[N]; void tarjan(int u){//稍有改动的tarjan模板 low[u]=dfn[u]=++dfscnt; Stack[++stack_top]=u; for(int i=h[u];i;i=e[i].next){ register int v=e[i].to; if (!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); } else if (!belong[v]) low[u]=min(low[u],dfn[v]); } if (low[u]==dfn[u]){ belong[u]=++col;num[col]=1;//别忘了这句啊 while (Stack[stack_top]!=u){ int v=Stack[stack_top]; belong[v]=col;--stack_top; num[col]++;//记录每个强联通分量所含顶点个数 } --stack_top;//别忘了把u弹出栈哦 } } int maxn,ans,i,n,m,x,y,t; int main(){ freopen("t1.in","r",stdin); scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&t); add(x,y);if (t==2) add(y,x); //建图,注意处理好有向图和无向图 } for(i=1;i<=n;i++) if (!dfn[i]) tarjan(i); //tarjan算法模板 for(i=1;i<=col;i++) maxn=max(maxn,num[i]); //求所有强联通分量所含顶点个数最大值 printf("%d",maxn); for(i=1;i<=n;i++) //从小到大枚举,保证字典序最小 if (num[belong[i]]==maxn){ printf("\n%d",i); for(int j=i+1;j<=n;j++) if (belong[i]==belong[j]) printf(" %d",j);break; } return 0; }
来源:https://blog.csdn.net/ZHUYINGYE_123456/article/details/100044701