• 分层图学习笔记


    第二版

    新增例题 代码详细注释,文字说明易懂,图片简明

    草鉴定Grass Cownoisseur

    首先看一眼,是单向边。。。

    我们要求最多能达到的草场数,而又有可以COW可以违反规定,自然而然的想到分层图,然而我们如何的保证跑出来的是最长路,而且走过的操场不重复,我们自然的想到tarjan缩点,这样保证了新图是一个DAG,这样再进行分层图复制,这样可以保证没有正权环(他就无环),然后跑一边spfa即可

    这题其实挺考码力的(猪国杀笑了)

    我们来理清思路,最重要的是分步来实现

    1. 读入数据并建图
    2. 对于原图做tarjan缩点
    3. 建一个缩完之后的DAG,并复制
    4. 再1,2两层中连接反向边
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int Maxn=1e5+11;
    int n,m,x,y,h[Maxn*2],cnt,p[2*Maxn],dis[Maxn*2];
    bool fsta[Maxn],vis[Maxn*2];
    struct Edge{
    	int fr,to,lac;
    }edge[Maxn*3],vedge[Maxn];
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(ch<'0'||ch>'9') ch=getchar();
    	while(ch>='0'&&ch<='9'){
    		x=(x<<1)+(x<<3)+(ch-'0');
    		ch=getchar();
    	}
    	return x;
    }
    void insert(int y,int x){
    	edge[cnt].to=y;
    	edge[cnt].fr=x;
    	edge[cnt].lac=h[x];
    	h[x]=cnt++;
    }
    int low[Maxn],dfn[Maxn],dep,sta[Maxn],top,col[Maxn],num;
    void tarjan(int u){
    	low[u]=dfn[u]=++dep;
    	fsta[u]=1,sta[++top]=u;
    	for(int i=h[u];i!=-1;i=edge[i].lac){
    		int to=edge[i].to;
    		if(dfn[to]){
    			if(fsta[to])	low[u]=min(low[u],dfn[to]);
    			continue;
    		}
    		tarjan(to);
    		low[u]=min(low[u],low[to]);	
    	}
    	if(low[u]==dfn[u]){
    		num++;
    		while(fsta[u]){
    			col[sta[top]]=num;
    			fsta[sta[top--]]=0;
    		}
    	}
    }
    bool spe(){
    	if(num==1){
    		printf("%d",n);
    		return 0;
    	}
    	return 1;
    }
    void dijkstra(){
    	queue<int> que;//这真的是spfa
    	que.push(col[1]);
    	while(!que.empty()){
    		int fr=que.front();
    		que.pop();
    		vis[fr]=0;
    		for(int i=h[fr];i!=-1;i=edge[i].lac){
    			int to=edge[i].to;
    			if(dis[to]<dis[fr]+p[to]){
    				dis[to]=dis[fr]+p[to];//其实再DAG里,点权可以当边权用了
    				if(!vis[to]){
    					vis[to]=1;
    					que.push(to);
    				}
    			}
    		}
    	} 
    	return;
    }
    int main(){
    //	freopen("Grass.in","r",stdin);
    	n=read(),m=read();
    	memset(h,-1,sizeof h);
    	for(int i=1;i<=m;i++)	insert(read(),read());
    	for(int i=1;i<=n;i++)	if(!dfn[i]) tarjan(i);
    	if(spe()){//特判只有一个强连通如果不的话,我们没有边。。
    		memset(h,-1,sizeof h);
    		memcpy(vedge,edge,sizeof vedge);
    		cnt=0;
    		for(int i=0;i<m;i++){
    			int fr=vedge[i].fr,to=vedge[i].to;
    			if(col[fr]==col[to]) continue;
    			insert(col[to],col[fr]);
    		}//建第一层 
    		for(int i=0;i<m;i++){
    			int fr=vedge[i].fr,to=vedge[i].to;
    			if(col[fr]==col[to]) continue;
    			insert(col[to]+num,col[fr]+num);
    		}//建第二层 
    		for(int i=0;i<m;i++){
    			int fr=vedge[i].fr,to=vedge[i].to;
    			if(col[fr]==col[to]) continue;
    			insert(col[fr]+num,col[to]);
    		}//连接一二层
    		for(int i=1;i<=n;i++) p[col[i]]++;
    		for(int i=1;i<=num;i++) p[num+i]=p[i];
    		dijkstra();//spfa......
    		printf("%d",dis[num+col[1]]);//输出最长路
    	}
    	return 0;
    }
    

    JLOI2011 飞行计划

    这是显然的分层图,我们注意到它满足分层图的性质,就是说再大部分正常时会有(k)次诡异的操作不花钱,我们就进行分层图就好了。

    样例是这样的。我们在这个图跑一下就好了。

    但是。。。。会有坑点。我们如果没用(k)次机会用0元到了t咋办。。。。

    所以我们1选择在每一层的t进行连一条权值嵬0一条的边

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    using namespace std;
    const int Maxm=2*1e6+5*1e5+2111,Maxn=1e5+11111;
    int n,m,k,s,t,x,y,z,h[Maxn],cnt,dis[Maxn];
    bool vis[Maxn];
    struct Point{
    	int id,dis;
    	bool operator <(const Point &x)const {
    		return x.dis<dis;
    	}
    	//堆优化
    };
    struct Edge{
    	int wg,to,lac;
    	void insert(int x,int y,int z){
    		to=y;
    		wg=z;
    		lac=h[x];
    		h[x]=cnt++;
    	}
    }edge[Maxm];
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(ch<'0'||ch>'9') ch=getchar();
    	while(ch<='9'&&ch>='0'){
    		x=(x<<1)+(x<<3)+(ch-'0');
    		ch=getchar();
    	}
    	return x;
    }
    void dijkstra(int s,int t){
    	//从第s到t跑dijkstra
    	priority_queue<Point> q;
    	//priority_queue
    	memset(dis,63,sizeof dis);
    	dis[s]=0;q.push((Point){s,0});
    	while(!q.empty()){
    		Point fr=q.top();q.pop();
    		if(vis[fr.id]) continue;
    		if(fr.id==t)//如果到了t点 
    			while(!q.empty()) q.pop();
    		vis[fr.id]=1;
    		for(int i=h[fr.id];i!=-1;i=edge[i].lac){
    			int to=edge[i].to;
    			if(dis[to]>dis[fr.id]+edge[i].wg){
    				dis[to]=dis[fr.id]+edge[i].wg;
    				//更新 进堆
    				q.push((Point){to,dis[to]}); 
    			}
    		}
    	}
    }
    int main(){
    	freopen("JLOIFLY.in","r",stdin);
    	n=read(),m=read(),k=read();s=read(),t=read();
    	memset(h,-1,sizeof h);
    	for(int j=1;j<=m;j++){
    		x=read();y=read();z=read();
    		for(int i=0;i<=k;i++){
    			edge[cnt].insert(i*n+x,i*n+y,z);
    			edge[cnt].insert(i*n+y,i*n+x,z);
    			//最后的标号是 0 - n-1+k*n.,k+1层 
    			//第i层的编号 (i-1)*n+x 
    			//然后在建每个层的连接
    			if(i!=k){
    				//最后一层就不建了 
    				edge[cnt].insert(i*n+x,(i+1)*n+y,0);
    				edge[cnt].insert(i*n+y,(i+1)*n+x,0);
    			}
    		}
    		//在每个图上正常建
    	}
    	for(int i=1;i<=k;++i){
            edge[cnt].insert(t+(i-1)*n,t+i*n,0);
        }//预防奇葩数据
    	dijkstra(s,t+n*k);
    	printf("%d",dis[t+n*k]);
    	return 0;
    }
    

    flag:第二版会补充JLOI 的飞行计划,本周就会有第二版,或许有第三版

    flag 完成了

  • 相关阅读:
    【BZOJ4826】【HNOI2017】影魔(扫描线,单调栈)
    【BZOJ4540】【HNOI2016】序列(莫队)
    【NOIP2017】列队(Splay)
    ZJOI2018酱油记
    【BZOJ4828】【HNOI2017】大佬(动态规划)
    【NOIP2017】宝藏(状态压缩,动态规划)
    【HDU4336】Card Collector (动态规划,数学期望)
    【HDU4652】Dice(数学期望,动态规划)
    【BZOJ4945】【NOI2017】游戏(搜索,2-sat)
    【BZOJ3714】Kuglarz(最小生成树)
  • 原文地址:https://www.cnblogs.com/zhltao/p/12293466.html
Copyright © 2020-2023  润新知