• 【2019.9.16】Za


    //主要是根据各种网上资料做笔记

    Floyd

    (f[i][j]):从(i)号顶点到(j)号顶点只经过前(k)号点的最短路程

    for (k=1;k<=n;k++)
      for (i=1;i<=n;i++)
        for (j=1;j<=n;j++)
          f[i][j] = min(f[i][j],f[i][k]+f[k][j]);
    

    (k)是阶段 所以必须位于最外层 而(i和j)为附加状态

    应用

    1. 给一个正权无向图,找一个最小权值和的环

      这一定是一个简单环 考虑环上编号最大的结点(u)

      f[u-1][x][y]((u,x), (u,y))共同构成了环。

      在 Floyd 的过程中枚举(u),计算这个和的最小值即可

      有向图的最小环问题 可枚举起点(s=1sim n) 执行对优化的(Dijsktra)求解单源最短路径(s)一定为第一个被从堆中取出节点 扫描(s)所有出边 扩展、更新完成后 令(d[s]=+infty) 然后继续求解 当s第二次被从堆中取出时 (d[s])就是经过点(s)的最小环长度

    POJ1734 sightseeing trip

    找最小环 并输出一个最小环方案

       #include<bits/stdc++.h>
       using namespace std;
       const int N=100+10,M=1e5+50,inf=0x3f3f3f3f;
       int n,m,mp[N][N],dis[N][N],pos[N][N];
       vector<int>path;
       void get_path(int x,int y){
       	if(!pos[x][y]) return;
       	get_path(x,pos[x][y]);
       	path.push_back(pos[x][y]);
       	get_path(pos[x][y],y);
       }
       
       int main(){
       //	freopen("in.txt","r",stdin);
       	scanf("%d%d",&n,&m);
       	int ans=inf;
       	memset(mp,inf,sizeof(mp));
       	for(int i=1;i<=n;++i) mp[i][i]=0;
       	for(int i=1,x,y,w;i<=m;++i)
       		scanf("%d%d%d",&x,&y,&w),mp[x][y]=mp[y][x]=w;
       	memcpy(dis,mp,sizeof(mp));
       	for(int k=1;k<=n;++k){
       		for(int i=1;i<k;++i)
       			for(int j=i+1;j<k;++j)
       				if((long long)dis[i][j]+mp[i][k]+mp[k][j]<ans){
       					ans=dis[i][j]+mp[i][k]+mp[k][j];
       					path.clear(),path.push_back(i);
       					get_path(i,j);path.push_back(j),path.push_back(k);
       				}
       		for(int i=1;i<=n;++i)
       			for(int j=1;j<=n;++j)
       			if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j],pos[i][j]=k;
       	}
       	if(ans==inf) return puts("No solution."),0;
       	for(int i=0;i<path.size();++i) printf("%d ",path[i]);
       	return 0;
       }
    
    1. 传递闭包

      已知一个有向图中任意两点之间是否有连边,要求判断任意两点是否连通

      按照 Floyd 的过程,逐个加入点判断一下。

      只是此时的边的边权变为 (1/0),而取 (min)变成了运算。

      再进一步用 bitset 优化,复杂度可以到 (Oleft(dfrac{n^3}w ight))

       for (k=1;k<=n;k++)
         for (i=1;i<=n;i++)
           for (j=1;j<=n;j++)
             f[i][j]|=f[i][k]&f[k][j];
    
       // std::bitset<SIZE> f[SIZE];
       for (k = 1; k <= n; k++)
         for (i = 1; i <= n; i++)
           if (f[i][k]) f[i] = f[i] & f[k];
    

    POJ1094 sorting it all out

    给定 n 个变量,m 个不等式。

    不等式之间具有传递性,即若 A>B 且 B>C ,则 A>C。

    判断这 m 个不等式是否有矛盾。

    若存在矛盾,则求出(T)的最小值,满足仅用前(T)个不等式就能确定不等式之间存在矛盾。

    若无矛盾,则判断这 m 个不等式是否能确定每一对变量之间的关系。

    若能,则求出(T)的最小值,满足仅用前(T)个不等式就能确定每一对变量之间的大小关系。

    ==不得不说 lyd劳斯的代码真的很好 精简高效

    (mp[i][j])表示(i<j)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100+10,M=1e5+50,inf=0x3f3f3f3f;
    int n,m,x,y,mp[N][N],ok1,ok2;
    char opt[10];
    int main(){
    	freopen("in.txt","r",stdin);
    	//freopen("and.out","w",stdout);
    	while(~scanf("%d%d",&n,&m)&&(n+m)){
    		ok1=ok2=0;
    		memset(mp,0,sizeof(mp));
    		int i;
    		for(i=1;i<=n;++i) mp[i][i]=1;
    		for(i=1;i<=m;++i){
    			scanf("%s",opt),ok2=0;
    			x=opt[0]-'A'+1,y=opt[2]-'A'+1,mp[x][y]=1;
    			for(int j=1;j<=n;++j)
    				for(int k=1;k<=n;++k)
    					mp[j][k]|=mp[j][x]&mp[y][k];
    			for(int j=1;j<n;++j)
    				for(int k=j+1;k<=n;++k)
    					if(mp[j][k]&mp[k][j]){
    						printf("Inconsistency found after %d relations.
    ",i);
    						j=n,ok1=1;break;
    					}
    					else if(!(mp[j][k]|mp[k][j])) ok2=1;
    			if(ok1) break;
    			if(!ok2){
    				printf("Sorted sequence determined after %d relations: ",i);
    				int a[N];
    				memset(a,0,sizeof(a)) ;
    				for(int j=1;j<=n;++j)
    				for(int k=j+1;k<=n;++k)
    				if(mp[j][k]) ++a[k];else ++a[j];
    				for(int j=0;j<n;++j)
    				for(int k=1;k<=n;++k)
    				if(a[k]==j) {putchar(k+'A'-1);break;}
    				puts(".");break;
    			}
    		}
    		if(i>m) puts("Sorted sequence cannot be determined.");
    	    for(++i;i<=m;++i) scanf("%s",opt);
    	}
    	return 0;
    }
    
    1. else

    POJ2613 cow relays

    给定一张由T条边构成的无向图,点的编号为1~1000之间的整数。

    求从起点S到终点E恰好经过N条边(可以重复经过)的最短路。

    矩阵乘法和floyd的组合!

       #include<bits/stdc++.h>
       using namespace std;
       const int N=200+10,M=1e6+10,inf=0x3f3f3f3f;
       int n,m,s,t,vc[M];
       struct mar{
       	int a[N][N];
       	mar operator *(mar &X){
       		mar c;
       		memset(c.a,inf,sizeof(c.a));
       		for(int i=1;i<=vc[0];++i)
       			for(int j=1;j<=vc[0];++j)
       				for(int k=1;k<=vc[0];++k)
       				c.a[i][j]=min(c.a[i][j],a[i][k]+X.a[k][j]);
       		return c;
       	}
       }ans,mp; 
       
       void qpow(mar a,int b){
       	ans=a,--b;
       	while(b){
       		if(b&1) ans=ans*a;
       		a=a*a,b>>=1;
       	}
       }
       
       int main(){
       //	freopen("in.txt","r",stdin);
       	scanf("%d%d%d%d",&n,&m,&s,&t);
       	memset(mp.a,inf,sizeof(mp.a));
       	for(int i=1,x,y,w;i<=m;++i){
       		scanf("%d%d%d",&w,&x,&y);
       		x=(!vc[x]?(vc[x]=++vc[0]):vc[x]),
       		y=(!vc[y]?(vc[y]=++vc[0]):vc[y]);
       		mp.a[x][y]=mp.a[y][x]=min(w,mp.a[x][y]);
       	}
       	//for(int i=1;i<=vc[0];++i) mp.a[i][i]=0;
       	qpow(mp,n);
       	printf("%d",ans.a[vc[s]][vc[t]]);
       	return 0;
       }
       
    

    SCOI2008 天平

    bzoj1077 luogu2447

    用floyd跑差分约束==

    因为砝码大小只有1、2、3 所以未知时最大差值为2 最小差值为-2

    (A+B>C+D)可以转为(A-C>D-B) 然后就挨个判断就好了

    注意判断等于时的条件

       #include<bits/stdc++.h>
       using namespace std;
       #define Max(x,y) ((x)>(y)?(x):(y))
       #define Min(x,y) ((x)<(y)?(x):(y))
       const int N=50+10,M=1e6+10,inf=0x3f3f3f3f;
       int n,A,B,c1,c2,c3,mx[N][N],mn[N][N];
       char opt[N];
       
       int main(){
       	freopen("in.txt","r",stdin);
       	scanf("%d%d%d",&n,&A,&B);
       	for(int i=1;i<=n;++i){
       		scanf("%s",opt+1);mx[i][i]=mn[i][i]=0;
       		for(int j=1;j<=n;++j)
       		if(j!=i){
       			if(opt[j]=='-') mn[i][j]=-2,mx[i][j]=-1;
       			else if(opt[j]=='+') mn[i][j]=1,mx[i][j]=2;
       			else if(opt[j]=='=') mx[i][j]=mn[i][j]=0;
       			else mn[i][j]=-2,mx[i][j]=2;
       		}
       	}
       	for(int k=1;k<=n;++k)
       		for(int i=1;i<=n;++i)
       			for(int j=1;j<=n;++j)
       			mn[i][j]=Max(mn[i][j],mn[i][k]+mn[k][j]),
       			mx[i][j]=Min(mx[i][j],mx[i][k]+mx[k][j]);
       	for(int i=1;i<=n;++i)
       	if(i!=A&&i!=B)
       	for(int j=i+1;j<=n;++j)
       	if(j!=A&&j!=B){
       		if(mn[A][i]>mx[j][B]||mn[B][i]>mx[j][A]) ++c1;
       		if(mn[i][A]>mx[B][j]||mn[i][B]>mx[A][j]) ++c3;
       		if(mn[A][i]==mx[A][i]&&mn[j][B]==mx[j][B]&&mn[A][i]==mn[j][B])++c2;
                       else if(mn[B][i]==mx[B][i]&&mn[j][A]==mx[j][A]&&mn[B][i]==mn[j][A])++c2;
       	}
       	printf("%d %d %d",c1,c2,c3);
       	return 0;
       }
    

    SPFA

    ==就不说了

    SPFA 的时间复杂度为(O(kM)(kapprox2))玄学),但理论上界(O(NM))

    SPFA 的优化之SLF即 Small Label First。

    即在新元素加入队列时,如果队首元素权值大于新元素权值,那么就把新元素加入队首,否则依然加入队尾。

    该优化在确实在一些图上有显著效果,其复杂度也有保证,但是如果有负权边的话,可以直接卡到指数级。

    Dijkstra

    如果有向图的边权值全为正数,那么有一种复杂度有保证的单源最段路
    算法——(Dijkstra) 算法 它的复杂度是(O(|E| log |V|))
    事实上,(Dijkstra) 算法的思想和 (Prim) 有很多类似之处 (Dijkstra) 算法
    维护了一个未访问的结点集合(T)以及一个从(s)到结点(u)的当前距离 (dist[u])

    实现

    主要思想是,将结点分成两个集合已确定最短路长度的,未确定的 一开始第一个集合里只有(S)

    1. 将除源外所有结点当前距离设置为$ ∞(,将源)s$的当前距离设置为(0)
      将当前节点设置为源(s)
    2. 从当前结点(u)开始,找出所有在未访问集合(T)中与(u)有边((u,v))的结
      (v)。如果(dist[u]+w[u][v])
    3. 将当前节点从(T)中删除,并且找到在(T)(dist)最小的结点设置为新的
      当前节点。
    4. 重复((2))((3))直到(T)成为空集。

    时间复杂度:只用分析集合操作, 次 delete-min , 次 decrease-key

    如果用暴力:(O(n^2+m)) 如果用堆 (O((n+m)log n))

    如果用 priority_queue:(O((n+m)log m)) (注:如果使用 priority_queue,无法删除某一个旧的结点,只能插入一个权值更小的编号相同结点,这样操作导致堆中元素是 (O(m))的)

    如果用线段树(ZKW 线段树): ((O(n+m)log n))

    如果用 Fibonacci 堆:(O(nlog n+m))(这就是为啥优秀了)。

    正确性

    它的正确性在于,在未访问集合(T)中结点的(dist)是从(s)开始经过已经
    访问集合中的结点到达它的最短路
    如果选出的当前结点(u)(dist)不是最终的最小值,那么它最终的最短
    路一定是要经过一个此时(T)中的其它结点再到(u)。这时那个结点的(dist)
    定要小于(u)(dist),这就和(u)(dist)最小的结点矛盾了!

    输出方案

    开一个pre数组,在更新距离的时候记录下来后面的点是如何转移过去的,算法结束前再递归地输出路径即可。

    比如 Floyd 就要记录pre[i][j] = k;,Bellman-Ford 和 Dijkstra 一般记录 pre[v] = u

    对比

    Floyd Bellman-Ford Dijkstra
    每对结点之间的最短路 单源最短路 单源最短路
    无负环的图 任意图 非负权图
    (O(N^3)) (O ( NM )) (O((N+M)log M))

    P3403 跳楼机

    P3403 跳楼机

    经过改造,srwudi的跳楼机可以采用以下四种方式移动:

    向上移动x层;向上移动y层;向上移动z层;回到第一层。

    一个月黑风高的大中午,DJL来到了srwudi的家,现在他在srwudi家的第一层,碰巧跳楼机也在第一层。DJL想知道,他可以乘坐跳楼机前往的楼层数。

    yyb:先只考虑只用(y,z)两种移动方式,它们一定能够到达一些楼层,
    那么这些楼层再只用(x)拓展就能够计算答案。
    那么我们这样子计算答案,设(dis[i])表示可以到达(mod x=i)楼层的最小值,

    很巧妙!同余最短路

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=100000+10,M=100000+10,inf=0x3f3f3f3f;
    int x,y,z;
    ll n,ans=0;
    template <class t>void rd(t &x){
        x=0;int w=0;char ch=0;
        while(!isdigit(ch)) w|=ch=='-',ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=w?-x:x;
    }
    
    int head[N],tot=0;
    struct edge{int v,w,nxt;}e[M<<2];
    void add(int u,int v,int w){
    	e[++tot]=(edge){v,w,head[u]},head[u]=tot;
    }
    
    queue<int>q;bool vis[N];
    ll dis[N];
    void spfa(){
    	memset(dis,inf,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	q.push(1),vis[1]=1,dis[1]=1;
    	while(!q.empty()){
    		int u=q.front();q.pop(),vis[u]=0;
    		for(int i=head[u],v,w;i;i=e[i].nxt)
    		if(dis[v=e[i].v]>dis[u]+(w=e[i].w)){
    			dis[v]=dis[u]+w;
    			if(!vis[v]) q.push(v),vis[v]=1;
    		}
    	}
    }
    
    int main(){
    //	freopen("in.txt","r",stdin);
    	rd(n),rd(x),rd(y),rd(z);
    	if(n==1||x==1||y==1||z==1) return printf("%lld",n),0;
    	for(int i=0;i<x;++i) add(i,(i+y)%x,y),add(i,(i+z)%x,z);
    	spfa();
    	for(int i=0;i<x;++i)
    		if(dis[i]<=n) ans+=(n-dis[i])/x+1;
    	printf("%lld",ans);
    	return 0;
    }
    
    

    [国家集训队]墨墨的等式

    和跳楼机那题是一样的==

    #include<bits/stdc++.h>
    using namespace std;
    #define Max(x,y) ((x)>(y)?(x):(y))
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define ll long long
    const int N=12+10,M=500000+10,inf=0x3f3f3f3f;
    int n,a[N];
    ll b1,b2,ans=0;
    template <class t>void rd(t &x){
        x=0;int w=0;char ch=0;
        while(!isdigit(ch)) w|=ch=='-',ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=w?-x:x;
    }
    
    int head[M],tot=0;
    struct edge{int v,w,nxt;}e[M*20];
    void add(int u,int v,int w){
    	e[++tot]=(edge){v,w,head[u]},head[u]=tot;
    }
    
    queue<int>q;bool vis[M];
    ll dis[M];
    void spfa(){
    	memset(dis,inf,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	q.push(0),vis[0]=1,dis[0]=0;
    	while(!q.empty()){
    		int u=q.front();q.pop(),vis[u]=0;
    		for(int i=head[u],v,w;i;i=e[i].nxt)
    		if(dis[v=e[i].v]>dis[u]+(w=e[i].w)){
    			dis[v]=dis[u]+w;
    			if(!vis[v]) q.push(v),vis[v]=1;
    		}
    	}
    }
    
    int main(){
    	freopen("in.txt","r",stdin);
    	rd(n),rd(b1),rd(b2);
    	for(int i=1;i<=n;++i) rd(a[i]);
    	sort(a+1,a+n+1);
    	for(int i=0;i<a[1];++i)
    		for(int j=2;j<=n;++j) add(i,(i+a[j])%a[1],a[j]);
    	spfa();
    	for(int i=0;i<a[1];++i){
    		if(dis[i]<=b1-1) ans-=(b1-1-dis[i])/a[1]+1;
    		if(dis[i]<=b2) ans+=(b2-dis[i])/a[1]+1;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    

    [USACO08JAN]电话线Telephone Lines

    • 法一:二分+01BFS
      很好想der 二分路径中边权从小到大排第k条的值 然后BFS时 不超过mid的边权为0 超过mid的为1

      二分+01BFS

    • 法二:分层最短路

      #include<bits/stdc++.h>
      using namespace std;
      #define Max(x,y) ((x)>(y)?(x):(y))
      #define Min(x,y) ((x)<(y)?(x):(y))
      const int N=1000+10,M=10000+10,inf=0x3f3f3f3f;
      int n,m,K;
      template <class t>void rd(t &x){
          x=0;int w=0;char ch=0;
          while(!isdigit(ch)) w|=ch=='-',ch=getchar();
          while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
          x=w?-x:x;
      }
      
      int head[N],tot=0;
      struct edge{int v,w,nxt;}e[M<<1];
      void add(int u,int v,int w){
      	e[++tot]=(edge){v,w,head[u]},head[u]=tot;
      }
      
      int dis[N][N];bool vis[N][N];
      struct node{int id,us;};
      queue<node>q;
      void spfa(){
      	memset(dis,inf,sizeof(dis));
      	memset(vis,0,sizeof(vis));
      	q.push((node){1,0}),vis[1][0]=1,dis[1][0]=0;
      	while(!q.empty()){
      		node nw=q.front();q.pop();
      		int u=nw.id,us=nw.us;vis[u][us]=0;
      		for(int i=head[u],v,w;i;i=e[i].nxt){
      			if(dis[v=e[i].v][us]>max(dis[u][us],(w=e[i].w))){//不用 
      				dis[v][us]=max(dis[u][us],(w=e[i].w));
      				if(!vis[v][us]) q.push((node){v,us}),vis[v][us]=1;
      			}
      			if(us<K&&dis[v][us+1]>dis[u][us]){
      				dis[v][us+1]=dis[u][us];
      				if(!vis[v][us+1]) q.push((node){v,us+1}),vis[v][us+1]=1;
      			}
      		}
      	}
      }
      
      int main(){
      	freopen("in.txt","r",stdin);
      	rd(n),rd(m),rd(K);
      	for(int i=1,u,v,w;i<=m;++i) rd(u),rd(v),rd(w),add(u,v,w),add(v,u,w);
      	spfa();
      	if(dis[n][K]==inf) return puts("-1"),0;
      	printf("%d",dis[n][K]);
      	return 0;
      }
      

      [CQOI2005]新年好

      重庆城里有 (n)个车站,(m)条双向公路连接其中的某些车站。每两个车站最多用一条公路连接,从任何一个车站出发都可以经过一条或者多条公路到达其他车站,但不同的路径需要花费的时间可能不同。在一条路径上花费的时间等于路径上所有公路需要的时间之和。

      佳佳的家在车站 ,他有五个亲戚,分别住在车站(a,b,c,d,e)。过年了,他需要从自己的家出发,拜访每个亲戚(顺序任意),给他们送去节日的祝福。怎样走,才需要最少的时间?

      ==分别dij求出(1,a,b,c,d)点到各点的最短路 然后枚举顺序

      我用的(next\_permutation)来搞全排列==

    #include<bits/stdc++.h>
    using namespace std;
    #define Max(x,y) ((x)>(y)?(x):(y))
    #define Min(x,y) ((x)<(y)?(x):(y))
    #define ll long long
    typedef pair<int,int>pii;
    const int N=50000+10,M=1e5+10,inf=0x3f3f3f3f;
    int n,m,a[10],ans=inf,nw=0;
    int b[10]={0,1,2,3,4,5};
    template <class t>void rd(t &x){
        x=0;int w=0;char ch=0;
        while(!isdigit(ch)) w|=ch=='-',ch=getchar();
        while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x=w?-x:x;
    }
    
    int head[N],tot=0;
    struct edge{int v,w,nxt;}e[M<<1];
    void add(int u,int v,int w){
    	e[++tot]=(edge){v,w,head[u]},head[u]=tot;
    }
    
    int dis[7][N];bool vis[N];
    priority_queue<pii,vector<pii>,greater<pii> >q;
    void dij(int id,int s){
    	memset(vis,0,sizeof(vis));
    	dis[id][s]=0,q.push(make_pair(0,s));
    	while(!q.empty()){
    		int u=q.top().second;q.pop();
    		if(vis[u]) continue;vis[u]=1;
    		for(int i=head[u],v,w;i;i=e[i].nxt){
    			if(dis[id][v=e[i].v]>dis[id][u]+(w=e[i].w)){
    				dis[id][v]=dis[id][u]+w;
    				q.push(make_pair(dis[id][v],v));
    			}
    		}
    	}
    }
    
    void cal(int id){
    	nw+=dis[b[id-1]][a[b[id]]];
    	if(id==5){ans=Min(ans,nw),nw=0;return;}
    	cal(id+1);
    }
    
    int main(){
    	freopen("in.txt","r",stdin);
    	rd(n),rd(m);
    	for(int i=1;i<=5;++i) rd(a[i]);a[0]=1;
    	for(int i=1,u,v,w;i<=m;++i) rd(u),rd(v),rd(w),add(u,v,w),add(v,u,w);
    	memset(dis,inf,sizeof(dis));
    	for(int i=0;i<=5;++i) dij(i,a[i]);
    	cal(1);
    	while(next_permutation(b+1,b+6))
    	cal(1);
    	printf("%d",ans);
    	return 0;
    }
    
    
  • 相关阅读:
    深入浅出JavaScript (一)初识
    “0”基础让你学会 GridView (一)
    VS 中PageLayout 属性设置
    ASP.NET Forms身份认证
    教务系统数据库设计 (一)
    深入浅出Javascript(三)创建自定义对象以及属性、方法
    .NET 中的Cache
    深入浅出JavaScript (二) 代码放置位置与执行顺序
    触发器特殊的存储过程
    ASP.NET与JavaScript轻松实现输入信息验证
  • 原文地址:https://www.cnblogs.com/lxyyyy/p/11526554.html
Copyright © 2020-2023  润新知