微信扫一扫打赏支持

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\n",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\n",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 }

 

posted @ 2017-08-26 05:32  范仁义  阅读(538)  评论(0编辑  收藏  举报