zoukankan      html  css  js  c++  java
  • CF1204C Anna, Svyatoslav and Maps

    题意简述

      题目链接

      给定一张n个点的有向图,一个长度为m的点号序列,表示图中一条路径(不保证是简单路径),要求选出其一个子序列,使得原序列是依次经过该子序列中所有点的一条最短路径,最小化子序列长度并输出该子序列。

    算法概述

      把原序列变成子序列,要使子序列长度最小化,则对于原序列中的点,当然是能删就删。

      动手画一画三个样例,不难发现一个结论:对于原序列中连续的三个点p,v,q,若dis[p,q]=dis[p,v]+dis[v,q],则v点可以删去。其中dis[i,j]表示点i,j之间的最短路径。

      结论的正确性不难证明,由于满足上述等式,则p->v->q是p,q之间的一条最短路径,则可以将v删去,充分性成立。

      若可以将v删去,则说明p->v->q是p,q之间的一条最短路径,那么必然满足dis[p,q]=dis[p,v]+dis[v,q],必要性成立。

      故结论成立。

      有了上述结论,我们就可以直接用Floyd算法预处理出任意两点间的最短路径,然后依次遍历原序列中每个点,判断若该点是否可以删去,能删则删。由于删点之后还可能要用到该点的前驱节点,普通队列无法处理,故我们可以用链表处理这个序列。

      时间复杂度O(n3+m)

    参考代码

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N=110,M=1e6+10;
    struct Node{
    	int pre,nex,dat;
    	#define pre(p) pot[p].pre
    	#define nex(p) pot[p].nex
    	#define dat(p) pot[p].dat
    }pot[M];
    
    void remove(int p)
    {
    	pre(nex(p))=pre(p);
    	nex(pre(p))=nex(p);
    	pre(p)=nex(p)=-1;
    }
    
    void insert(int x,int p,int val)
    {
    	pre(p)=x;
    	nex(p)=-1;
    	nex(x)=p;
    	dat(p)=val;
    }
    
    int g[N][N];
    int dis[N][N];
    int n,m;
    
    void floyd()
    {
    	memset(dis,0x3f,sizeof dis);
    	
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    		{
    			if(i==j)dis[i][j]=0;
    			else if(g[i][j]==1)dis[i][j]=1;
    		}
    	
    	for(int k=1;k<=n;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];
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=n;j++)
    			scanf("%1d",&g[i][j]);
    	
    	floyd();
    	
    	scanf("%d",&m);
    	pre(0)=-1;
    	for(int i=1;i<=m;i++)
    	{
    		int p;scanf("%d",&p);
    		insert(i-1,i,p);
    	}
    	
    	int cnt=m;
    	for(int i=2;i<=m-1;i++)
    	{
    		int last=dat(pre(i)),next=dat(nex(i));
    		int now=dat(i);
    		if(dis[last][next]==dis[last][now]+dis[now][next])remove(i),cnt--;
    	}
    	
    	printf("%d
    ",cnt);
    	for(int i=nex(0);~i;i=nex(i))
    		printf("%d ",dat(i));
    	
    	return 0;
    }
    

      

  • 相关阅读:
    跟我一起学算法——贪心算法
    跟我一起学算法——最大流
    跟我一起学算法——红黑树
    设计模式总结
    敏捷开发模型
    TCP安全,SYN Flooding 和 nmap
    ICMP和重定向攻击
    IP安全,DDoS攻击、tearDrop攻击和微小IP碎片攻击
    Netfilter,获取http明文用户名和密码
    Linux常用网络命令
  • 原文地址:https://www.cnblogs.com/ninedream/p/13446498.html
Copyright © 2011-2022 走看看