zoukankan      html  css  js  c++  java
  • qbxt Day 4

    Day 4

    考试题解

    T1 油箱

    签到失败555。考试时一直卡在如何判定所有的点连成一个环,其实反向建个图就搞定了。其实我当时的思路是判定完联不联通然后跑(Dijkstra)去找最小环上的最大值,但是一直卡在判环上。谁知道没有-1的数据点,早知道我直接上(Dijkstra)(A)了。其实这也很好想,要是真正的考试,不可能让不会做的选手输出-1从而得到很多分,我觉得最多也就5分。所以我其实应该选择打上这个的,而不是打60跑路。(我其实在担心捆绑测试卡死我)

    正解

    二分答案,问题变成判断一个有向图能否任意两点联通。相当于判断点1是否能到达所有点并且所有点都能到达点1。可以建立正向图和反向图,并从点1开始dfs或bfs遍历。(反向建图操作确实骚)

    也可以直接上(tarjan)但是我不会。

    bfs+二分代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int N=50005,M=400005;
    int n,m,tot1,tot2,maxx,ans;
    int ver1[M],head1[N],edge1[M],Next1[M],d1[N],ver2[M],head2[N],edge2[M],Next2[M],d2[N];
    bool v1[N],v2[N];
    queue<int> q1,q2;
    void add1(int x,int y,int z){
    	ver1[++tot1]=y;
    	edge1[tot1]=z;
    	Next1[tot1]=head1[x];
    	head1[x]=tot1;
    }
    void add2(int x,int y,int z){
    	ver2[++tot2]=y;
    	edge2[tot2]=z;
    	Next2[tot2]=head2[x];
    	head2[x]=tot2;
    }
    void bfs1(int sum){
    	memset(v1,0,sizeof(v1));
    	q1.push(1);
    	v1[1]=1;
    	while(!q1.empty()){
    		int x=q1.front();
    		q1.pop();
    		for(int i=head1[x];i;i=Next1[i]){
    			int y=ver1[i],z=edge1[i];
    			if(v1[y]) continue;
    			if(z<=sum){
    				v1[y]=1;
    				q1.push(y);
    			}
    		}
    	}
    }
    void bfs2(int sum){
    	memset(v2,0,sizeof(v2));
    	q2.push(1);
    	v2[1]=1;
    	while(!q2.empty()){
    		int x=q2.front();
    		q2.pop();
    		for(int i=head2[x];i;i=Next2[i]){
    			int y=ver2[i],z=edge2[i];
    			if(v2[y]) continue;
    			if(z<=sum){
    				v2[y]=1;
    				q2.push(y);
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1,x,y,z;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		add1(x,y,z);
    		add2(y,x,z);
    		maxx=max(maxx,z);
    	} 
    	bfs1(maxx);
    	bfs2(maxx);
    	for(int i=1;i<=n;i++){
    		if(!v1[i]||!v2[i]){
    			printf("-1
    ");
    			return 0;
    		}	
    	}
    	int l=0,r=maxx;
    	while(l<=r){
    		int mid=(l+r)>>1;
    		bfs1(mid);
    		bfs2(mid);
    		int t=0;
    		for(int i=1;i<=n;i++){
    			if(!v1[i]||!v2[i]){
    				t=1;
    				l=mid+1;
    				break;
    			}
    		}
    		if(!t){
    			r=mid-1;
    			ans=mid;
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    T2 求和

    20分做法:

    直接暴力模拟:(O(n^2*m))

    40分做法:

    前缀和优化。

    100分做法:

    推出玄学式子。(好像有点懵b)

    序列修改的话:线段树。树的话:树链剖分。

    T3 染色

    动态规划

    没听懂???????????????????

    T4 数字

    看成不断添加一个新数字,这样相当于好数组成的哈密顿通路(不重复的经过每一个点)。那么只需要求出两两好数之间最短距离,然后状压dp求出哈密顿通路。

    把每一条边看作新加入一个数字,然后边权就是这个数

    如果状态转移的时候出现坏数,那么划掉这个数

    图论

    最短路算法

    floyd算法

    多源最短路算法,可以计算一个图中任意两点最短路。实质是(dp)。时间复杂度:(O(n^3))。空间复杂度:(O(n^2))。用邻接矩阵存图,由于其(dp)本质不能处理负环。

    可以用来求最小环。设(k)是最小环中编号最大的那一个点,那么从(i)(j)的最短路在这之前只能由小于(k)的编号的点中转得来,所以符合(k)是编号最大的点。求完之后再更新一遍最短路。

    核心代码如下

    for(int k=1;k<=n;k++){
    		for(int i=1;i<k;i++){
    			for(int j=i+1;j<k;j++){
    				res=min(res,dis[i][j]+mp[i][k]+mp[k][j]);
    			}
    		}
    		for(int i=1;i<=n;i++){
    			for(int j=1;j<=n;j++){
    				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
    			}
    		}
    	} 
    

    Dijkstra算法

    采用贪心策略,因此无法处理负权边。堆优化之后时间复杂度变为(O(mlogm))。但不优化的时间复杂度是(O(n^2))。也就是在稠密图中不优化反而更高效。

    SPFA

    可以判断负环:如果某个点入队n次则说明存在负环。时间复杂度是(O(mlohm)),但在特殊构造的数据下可能会退化为(O(mn))

    总结:floyd、SPFA可以处理负边权,SPFA可以判负环,floyd可以判最小环

    习题

    是否在最短路上

    最短路个数这个题类似,(if(f_{i,j}==f_{i,k}+d_{k,j}+f_{k,j}))

    牛 poj3660

    用floyd传递闭包判断两点是否联通。(f_{i,j}=f_{i,k}&f_{k,j})(f)数组中式bool变量,用来判断两点是否联通。

    floyd经典应用(经过(k)条边的最短路)bzoj1706

    倍增floyd

    牛:分层图 bzoj 1579

    加油:分层图 poj 3635

    每次只有两种操作,加一个油或者前进一步。

    Tales

    奇最短路和偶最短路(讲过)

    逛公园

    (dp_{u,k})表示从点1走到点(u)比最短路长(k)的路径条数。先求一遍最短路,然后(dp)转移:(dp_{u,k})=(sum(v->u,w)) $dp_{v,dis_u+k-w-dis_v_}。

    但是如果边在最短路上则不太好判断状态转移顺序,则可以使用记忆化搜索,自动按照拓扑序。如果存在零环,那么则有无穷多条路径满足条件。

    打怪

    看到(p)<=13,知道要状压记状态。但是由于没有拓扑序,所以要用最短路算法Dijkstra求拓扑序。

    次短路

    多几个(if)判断即可(严格次小生成树中有类似的分类讨论)

    差分约束

    求解查分约束系统就是单源最短路或者最长路问题。对于形如(x_i<=x_j+c)的形式,建一条从(j)(i)权值为(c)的边,最终求(x_t-x_s)的最小值就是求单源最短路,即为最紧的限制。

    例题 糖果

    最小生成树

    Kruscal:贪心,时间复杂度(O(mlogm))。适用于稀疏图。

    魔术师

    考虑前缀和(sum),区间([l,r])中个数奇偶性相当于(sum_l-1)(sum_r)奇偶性是否相同。相当于在(l-1)(r)之间连一条边,如果知道连通块中一个点的值,那么通过奇偶性的变化就可以推得哪一个杯子里有球,那么求出(sum_0)(sum_n)的最小生成树就是最终的答案。

    比如说((sum2-sum1)%2=1)((sum3-sum1)%2=0)。那么(sum3)底下一定有球。每次询问相当于加一条边,而只有每一个块都联通之后,才能知道每一个杯子底下到底有没有球。

    货车运输

    求出最大瓶颈生成树,然后所求即为生成树上一条路径中的最小边权。

    通过倍增解决。

    树上倍增

    可以求(LCA)链和,链上最大最小值。

    Tarjan求LCA

    没听懂,时间复杂度是(O(n))

    严格次小生成树

    每加入一条边

    维护一种数据结构:支持更改一个点的点权,求一个子树的最小点权,换根。

    拓扑问题

    如果点(a)可以到达点(b),那么在拓扑序中点(a)在点(b)前面。可以按照拓扑顺序来(dp)。这样就可以避免需要某一个点的值转移得来,但这个点还没有求过。

    求拓扑排序:用一个队列来维护,先将所有入度为0的点加入队列。每次弹出队首,然后删掉它的所有出度。如果出现入度为0的点,那么入队。直到队列为空,就得到了拓扑序。

    拓扑排序判环:在图上运行拓扑排序算法,(但一般是dfs判环)

    用优先队列维护下标,每次拿出下标最小的入度为0的节点,这样就可以得到字典序最小的拓扑序。

    求一个拓扑图从一个点出发的路径条数

    (f_i)表示从(i)点出发的路径条数,(f_i)=sigma f_j+1(,其中)i(到)j(有一条边。用拓扑序的逆序求)f$值。

    求一个拓扑图中从S点到T点的必经边。

    求出(S)到每个点的路径条数(f_i),以及每个点到(T)的路径条数(g_i)。如果一条x到y的边满足(f_x*g_y)=
    那么为必经边。

    SDOI 2009 Elaxia的路线

    ??????????????

    dilsorth定理

    设拓扑序中可以用(n)个链来覆盖所有的点,那么(n)=最长反链长度。

    导弹拦截:第二问相当于每个导弹向不高于自己的导弹连边,求最少链覆盖所有边。根据定理,相当于最长反链,即最长上升子序列。

    垃圾

    只能往右或往下,相当于只能从左边或上边转移而来。连边遍历拓扑序。相当于求一个最小链覆盖,从右下角往左上角走,求出最长反链的长度即可。

    ?无题

    建一个源点(S)和汇点(T),分别向所有点连边,

    ??????????????????????

    二分图

    一个无向图可以被分为两个点集,且点集内部没有连边。等价于图中没有奇环。

    (dfs)对整个二分图染色

    关押罪犯

    二分答案+dfs染色

    匈牙利算法(二分图最大匹配)

    ?????????(得学)

    棋盘

    ?????????????????

    二分图的最小覆盖=最大匹配

    求最大独立集=总点数-最大匹配

    机器调度

    还没看完题就讲完了????

    棋盘2

    (n*n)棋盘上可以放的最多的不会相互攻击的骑士数量。求最大独立集。

  • 相关阅读:
    (三)java程序的编译和执行
    (二)java环境搭建
    (一)java概述
    (一)mvc与mvvm设计模式
    小程序 开发之向左滑动实现删除功能
    小程序开发之三级联动
    (效果五)js获取客户端ip地址及浏览器信息
    (六)js常见四大排序
    小程序开发之组件的使用
    Nodejs实现爬虫抓取数据
  • 原文地址:https://www.cnblogs.com/57xmz/p/13768978.html
Copyright © 2011-2022 走看看