zoukankan      html  css  js  c++  java
  • P1137 旅行计划

    P1137 旅行计划

    题目描述

    小明要去一个国家旅游。这个国家有N个城市,编号为1~N,并且有M条道路连接着,小明准备从其中一个城市出发,并只往东走到城市i停止。

    所以他就需要选择最先到达的城市,并制定一条路线以城市i为终点,使得线路上除了第一个城市,每个城市都在路线前一个城市东面,并且满足这个前提下还希望游览的城市尽量多。

    现在,你只知道每一条道路所连接的两个城市的相对位置关系,但并不知道所有城市具体的位置。现在对于所有的i,都需要你为小明制定一条路线,并求出以城市i为终点最多能够游览多少个城市。

    输入输出格式

    输入格式:

    输入的第1行为两个正整数N, M。

    接下来M行,每行两个正整数x, y,表示了有一条连接城市x与城市y的道路,保证了城市x在城市y西面。

    输出格式:

    输出包括N行,第i行包含一个正整数,表示以第i个城市为终点最多能游览多少个城市。

    输入输出样例

    输入样例#1:
    5 6
    1 2
    1 3
    2 3
    2 4
    3 4
    2 5
    
    输出样例#1:
    1
    2
    3
    4
    3
    

    说明

    均选择从城市1出发可以得到以上答案。

    对于20%的数据,N ≤ 100;

    对于60%的数据,N ≤ 1000;

    对于100%的数据,N ≤ 100000,M ≤ 200000。

    分析:

    方法一:

    拓扑结构+简单dp(其实可以边拓扑边dp)

    1、用栈实现拓扑结构,找出拓扑序列

    2、在这个拓扑序列的基础上,找所有i节点西边的点中经过城市最多的点+1,就是i对应的答案

    个人感觉verctor数组存图要比数组模拟链表存图方便很多。

     1 #include<cstdio>  
     2 #include<stack>  
     3 #include<vector>  
     4 using namespace std;  
     5   
     6 int n,m,x,y,ru[100001],ans[100001];  
     7 vector<int> ro[100001],tot;  
     8 stack<int> q;  
     9   
    10 int main()  
    11 {  
    12     scanf("%d%d",&n,&m);  
    13     for(int i=1;i<=m;i++)  
    14     {  
    15         //读入数据 
    16         scanf("%d%d",&x,&y);  
    17         //y节点的入度加1 
    18         ru[y]++;  
    19         //相当于x多了个孩子 
    20         ro[x].push_back(y);  
    21     }  
    22     //下面是拓扑排序,用栈实现,其实和队列实现差不多 
    23     //如果入度为0,入栈,对应的答案为1 
    24     for(int i=1;i<=n;i++)  
    25       if(!ru[i]) q.push(i),ans[i]=1;  
    26     //当q中还有元素的时候 
    27     while(!q.empty())  
    28     {  
    29         //取栈最上面的元素 
    30         int k=q.top();q.pop();  
    31         tot.push_back(k);  
    32         //找所有连接的边 
    33         for(int i=0;i<ro[k].size();i++)  
    34         {  
    35             //如果入度为空,继续整入栈 
    36             ru[ro[k][i]]--;if(!ru[ro[k][i]]) q.push(ro[k][i]);  
    37         }   
    38     } 
    39     //遍历这n个点,简单dp 
    40     for(int i=0;i<tot.size();i++)  
    41     {  
    42         int k=tot[i];  
    43         //找这个点的所有入度的点 
    44         for(int j=0;j<ro[k].size();j++)  
    45         {  
    46             //找到这些点中节点数最大的,+1 
    47             int z=ro[k][j];  
    48             if(ans[z]<ans[k]+1) ans[z]=ans[k]+1;  
    49         }  
    50     }  
    51     for(int i=1;i<=n;i++) printf("%d
    ",ans[i]);  
    52     return 0;  
    53 }  

    方法二:

    栈实现spfa

    1、找到起点

    2、从起点处做spfa,用出栈的点去更新别的所有点

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<stack>
     5 using namespace std;
     6 #define maxm 200010
     7 int n,m,ans,num,head[maxm],dp[100010],ru[100010];
     8 stack<int>q;
     9 bool vis[100010];
    10 //
    11 struct node{
    12     int pre,to;
    13 }e[maxm];
    14 //数组模拟链表 
    15 void Insert(int from,int to){
    16     e[++num].to=to;
    17     e[num].pre=head[from];
    18     head[from]=num;
    19 }
    20 void spfa(){
    21     //栈中还有元素 
    22     while(!q.empty()){
    23         //元素出栈 ,表示不再栈中 
    24         int point=q.top();q.pop();vis[point]=0;
    25         //遍历point的所有出度的边 
    26         for(int i=head[point];i;i=e[i].pre){
    27             int to=e[i].to;
    28             //用这个节点去更新所有节点 
    29             if(dp[to]<dp[point]+1){
    30                 dp[to]=dp[point]+1;
    31                 if(!vis[to]){
    32                     vis[to]=1;//表示在栈中 
    33                     //被更新的节点继续入栈 
    34                     q.push(to);
    35                 }
    36             }
    37         }
    38     }
    39 }
    40 //读数 
    41 int qread(){
    42     int i=0;
    43     char ch=getchar();
    44     while(ch<'0'||ch>'9')ch=getchar();
    45     while(ch<='9'&&ch>='0'){i=i*10+ch-'0';ch=getchar();}
    46     return i;
    47 }
    48 int main(){
    49     //读数据 
    50     n=qread();m=qread();
    51     int x,y;
    52     for(int i=1;i<=m;i++){
    53         x=qread();y=qread();
    54         //插入x到y的边 
    55         Insert(x,y);
    56         //y的入度加1
    57         //入度出度这个写在读入数据这里是极好的 
    58         ru[y]++;
    59     }
    60     //遍历n个节点,将空节点先入栈 
    61     for(int i=1;i<=n;i++){
    62         if(ru[i]==0){
    63             q.push(i);
    64             vis[i]=1;
    65             //并且把值置为1 
    66             dp[i]=1;
    67         }
    68     }
    69     //做spfa 
    70     spfa();
    71     for(int i=1;i<=n;i++)printf("%d
    ",dp[i]);
    72 }

    方法三:

    记忆化搜索

    这个题只能向一边走而不能走环,所以满足无后效性原理,显然可以动态规划,一般题目显然搜索比较好(ˇˍˇ) 想;但是搜索往往会是指数级别的时间复杂度,这个题的最后几组数据显然不支持指数级别的算法,但是记忆化搜索可以满足需求,就是记忆每一次搜索的结果,可以很快速的完成搜索,而且记忆化搜索的时间复杂度和动归理论上是一样的,于是很多动归题目实在想不出来都可以记忆化搜索,有时还能减支优化。

     1 #include<iostream>
     2 #include<cstdlib>
     3 #include<cstdio>
     4 #include<vector>
     5 #include<algorithm>
     6 #define MAXN 400010
     7 using namespace std;
     8 vector<int>l[MAXN];
     9 int n,m,u[MAXN],v[MAXN],dp[MAXN],cnt=1;
    10 void add(int x,int y)
    11   {
    12         u[++cnt]=x;v[cnt]=y;
    13         //把边的编号信息加入东边的那个点 
    14         l[x].push_back(cnt);
    15   }
    16 int dfs(int x)
    17  {
    18      int i;
    19      //如果搜索过x这个点,就返回 
    20      if(dp[x])return dp[x];
    21      //初始值置为1 
    22      dp[x]=1;
    23      //找所有x点的入边 
    24      for(i=l[x].size()-1;i>=0;i--)
    25        {
    26           int k=l[x][i];
    27           //所有入边中的最大值+1即为结果
    28           //其实是在边搜索边dp 
    29          dp[x]=max(dp[x],dfs(v[k])+1);    
    30       }
    31     return dp[x];
    32  }
    33 int main()
    34   {
    35        int i,j,k;
    36        //读数据 
    37        scanf("%d%d",&n,&m);
    38        for(i=1;i<=m;i++)
    39          {
    40             int a,b;
    41           scanf("%d%d",&a,&b);
    42           //加边 
    43           add(b,a);    
    44        }
    45        //记忆化搜索 
    46      for(i=1;i<=n;i++)
    47            cout<<dfs(i)<<endl;
    48      return 0;
    49 }

    转载于:https://www.cnblogs.com/Renyi-Fan/p/7433161.html

  • 相关阅读:
    svn command line tag
    MDbg.exe(.NET Framework 命令行调试程序)
    Microsoft Web Deployment Tool
    sql server CI
    VS 2010 One Click Deployment Issue “Application Validation did not succeed. Unable to continue”
    mshtml
    大厂程序员站错队被架空,只拿着五折工资!苟活和离职,如何选择?
    揭秘!Windows 为什么会蓝屏?微软程序员竟说是这个原因...
    喂!千万别忘了这个C语言知识!(~0 == -1 问题)
    Linux 比 Windows 更好,谁反对?我有13个赞成理由
  • 原文地址:https://www.cnblogs.com/twodog/p/12139540.html
Copyright © 2011-2022 走看看