运输成本
题意
给出一个有向图,如果点与点之间相互可达,那么经过这些路径不计费,求从某点出发,该图的最长路径是多少。
思路
tarjan强连通算法缩点,相互可达的看成一个点,重新建图,再用拓扑排序求最长路
Ps:存储空间有修改,其他都一样,空间太大本地编译器无法编译。
洛谷P3387有道一模一样的题,模板,稍微改一下就行。
两个模板基本没什么变动,前面一直在尝试删边让他不连通,但是发现不对,随后索性缩点,重建图,因为重建图常数会变大,前面一直不敢尝试
if (Belong[x] != Belong[y])
{
add_edge(Belong[x] + n, Belong[y] + n, z);
indegree[Belong[y] + n]++;
}
这是缩点重建图的关键
#include <bits/stdc++.h>
using namespace std;
struct Edge
{
int u, v, w;
};
class Solver
{
public:
int head[3005], next[3005], tov[3005], dis[3005], num;
int indegree[1005];
void add_edge(int from, int to, int len)
{
next[++num] = head[from];
tov[num] = to;
dis[num] = len;
head[from] = num;
}
int ins[1005], idx, Bcnt;
int dfn[1005], low[1005];
int Belong[1005];
stack <int> s;
void tarjan(int u)
{
int v;
dfn[u] = low[u] = ++idx;//每次dfs,u的次序号增加1
s.push(u);//将u入栈
ins[u] = 1;//标记u在栈内
for (int i = head[u]; i != -1; i = next[i]) //访问从u出发的边
{
v = tov[i];
if (!dfn[v])//如果v没被处理过
{
tarjan(v);//dfs(v)
low[u] = min(low[u], low[v]);//u点能到达的最小次序号是它自己能到达点的最小次序号和连接点v能到达点的最小次序号中较小的
}
else if (ins[v])
{
low[u] = min(low[u], dfn[v]);//如果v在栈内,u点能到达的最小次序号是它自己能到达点的最小次序号和v的次序号中较小的
}
}
if (dfn[u] == low[u])
{
Bcnt++;
do
{
v = s.top();
s.pop();
ins[v] = 0;
Belong[v] = Bcnt;
} while (u != v);
}
}
int a[1005], cnt;
void topu(int n)
{
queue<int> q;
cnt = 1;
for (int i = n + 1; i <= n + Bcnt - 1; i++)
if (indegree[i] == 0)
q.push(i);
int u;
while (!q.empty())
{
u = q.front();
a[cnt++] = u; //将上边选出的没有依赖顶点的节点加入到排序结果中
q.pop(); //删除队顶元素
for (int i = head[u]; i != -1; i = next[i])
{
int v = tov[i];
indegree[v] --; //删去以u为顶点的边
if (indegree[v] == 0) //如果节点i的所有依赖顶点连接边都已经删去
q.push(v); //即变为无依赖顶点的节点 将其入队
}
}
}
void init()
{
memset(head, -1, sizeof(head));
memset(next, 0, sizeof(next));
memset(tov, 0, sizeof(tov));
memset(dis, 0, sizeof(dis));
num = 0;
memset(indegree, 0, sizeof(indegree));
memset(ins, 0, sizeof(ins));
idx = 0, Bcnt = 0;
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(Belong, 0, sizeof(Belong));
memset(a, 0, sizeof(a));
}
public:
int solve(int n, const vector<Edge> &edges)
{
/********** Begin **********/
init();
int m = edges.size();
for (int i = 0; i<m; i++)
{
int x, y, z;
x = edges[i].u, y = edges[i].v, z = edges[i].w;
add_edge(x, y, z);
}
for (int i = 1; i <= n; i++)
add_edge(0, i, 1);
tarjan(0);
for (int i = 1; i <= n; i++)
printf("%d ", Belong[i]);
cout << endl;
num = 0;
memset(head, -1, sizeof(head));
for (int i = 0; i<m; i++)
{
Edge t = edges[i];
int x = t.u, y = t.v, z = t.w;
if (Belong[x] != Belong[y])
{
add_edge(Belong[x] + n, Belong[y] + n, z);
indegree[Belong[y] + n]++;
}
}
topu(n);
int max_dis[1005] = { 0 }, Max = 0;
for (int i = cnt - 1; i >= 1; i--)
{
int u = a[i];
for (int j = head[u]; j != -1; j = next[j])
{
int v = tov[j], len = dis[j];
max_dis[u] = max(max_dis[u], max_dis[v] + len);
}
Max = max(Max, max_dis[u]);
}
return Max;
/********** End **********/
}
};
int main()
{
Solver xx;
int n, m;
cin >> n >> m;
vector<Edge>yy;
for (int i = 0; i<m; i++)
{
int x, y, z;
cin >> x >> y >> z;
Edge t;
t.u = x, t.v = y, t.w = z;
yy.push_back(t);
}
cout << xx.solve(n, yy) << endl;
return 0;
}