【题目】
题目描述:
小 A 要进行一次旅行。这回他要在序号为 1 到 n 的 n 个城市之间旅行。这 n 个城市之间共有 m 条连接两个城市的单行公路,对于第 i 条公路的风景有一个评分 ai。小 A 有一个要求:挑选旅行路线时经过某条路时看到的风景比上一条经过的公路的风景评分更高。小 A 想看到尽可能多的风景,请你告诉他他能找到的最长的满足他要求旅行路线有多长。(每条公路长度视为1,可以重复经过一个城市)
输入格式:
第一行为两个整数数 n,m。
接下来 m 行,每行三个整数 xi,yi,ai 分别代表每条公路的起点序号、终点序号、风景分数。
输出格式:
输出一个整数,为满足要求的最长路线的长度。
样例数据:
输入
3 3 1 2 1 2 3 2 3 1 3
输出
3
备注:
【样例说明】
最长路径为:(1->2->3->1)
【数据范围】
对 30% 的输入数据,n ≤ 10;m ≤ 50;ai ≤ 100
对 60% 的输入数据,n ≤ 1000;m ≤ 5000;ai ≤ 1000
对 100% 的输入数据,n,m ≤ 300000;1 ≤ ai ≤ 100000
【分析】
emmm,一道非常巧妙的题
用 f [ i ] 表示到达 i 时,最长路线的长度
我们将边从小到大排序,依次加边,这样做的好处是每次加边时就可以直接 f [ v ] = max ( f [ v ] , f [ u ] + 1 ),就不用判断边的大小关系了,因为后加进来的边肯定比之前加进来的边大
还有一个小细节是如果有权值相等的边的话,这样可能会出错,具体可以用这组数据感受一下“4 3;1 2 1;2 3 2;3 4 2”(分号代表换行)这个时候我们就用一个临时数组 g 来记录还没加这些边时的 f,方程改成 f [ v ] = max ( f [ v ] , g[ u ] + 1 )
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
using namespace std;
int g[N],f[N];
int t,first[N],u[N],v[N],next[N];
void add(int z)
{
t++;
next[t]=first[z];
first[z]=t;
}
int main()
{
// freopen("travel.in","r",stdin);
// freopen("travel.out","w",stdout);
int n,m,i,j,w,ans=0;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&u[i],&v[i],&w);
add(w); //按照边权建链表,这样之后从前往后枚举就达到了排序的目的
}
for(i=1;i<=100000;++i)
{
for(j=first[i];j;j=next[j])
g[u[j]]=f[u[j]]; //建立临时数组
for(j=first[i];j;j=next[j])
f[v[j]]=max(f[v[j]],g[u[j]]+1); //转移
}
for(i=1;i<=n;++i)
ans=max(ans,f[i]);
printf("%d",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}