2019.08.23 日常总结
作者:互联网
洛谷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;
}
标签:2019.08,联通,23,int,农场,输出,村庄,分量,日常 来源: https://blog.csdn.net/ZHUYINGYE_123456/article/details/100044701