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 }
  • 相关阅读:
    zblog如何更改数据库配置以及生效
    阿里云RDS数据库改造迁移方案
    如何突破微信的支付额度限制
    IIS进行URL重写——实现https重定向,文件类型隐藏访问重写,nodejs等服务重写等等
    windows设置本地域名解析
    自己写的加密网页,与百度网盘私密很相似,需要密码才能访问(原创)
    CSS3 translate、transform、transition区别
    IIS前端页面不显示详细错误解决方法
    CSS滚动条设置
    IIS支持PHP文件解析
  • 原文地址:https://www.cnblogs.com/Renyi-Fan/p/7433161.html
Copyright © 2011-2022 走看看