抢掠计划
算法
先转化题意
在有 \(n\) 个点 \(m\) 条边的有向图上, 从点 \(S\) 开始, 最终到达一个拥有酒吧的点
求途径最大点权和, 其中可以重复经过一个点, 但是点权和不再计算
现在动用一下注意力, 在抽象有向图上不好处理, 我们考虑 \(\rm{DAG}\) 的情况
显然的, 对于一个 \(\rm{DAG}\) , 问题变得简单, 只需要按拓扑序跑 \(\rm{dp}\) 即可
那么考虑将原图缩点成为 \(\rm{DAG}\) 之后怎么处理
显然, 我们直接记录缩点之后的边权和即可
看题解发现 \(\rm{spfa}\) 跑最长路也是可以的
代码
当做强连通分量的板子练一下
#include <bits/stdc++.h>
const int MAXN = 5e5 + 20;
const int MAXM = 5e5 + 20;
int N, M;
int S, P;
class Graph_Class
{
private:
public:
struct node
{
int to, w;
int next;
} Edge[MAXM];
int Val[MAXN];
int Edge_Cnt = 0;
int head[MAXN];
bool End[MAXN]; // 是否为酒吧
int InDegree[MAXN];
void head_init() { for (int i = 1; i <= N; i++) head[i] = -1; }
void addedge(int u, int v) {
Edge[++Edge_Cnt].to = v;
Edge[Edge_Cnt].next = head[u];
head[u] = Edge_Cnt;
}
};
Graph_Class Graph1; // 原图
Graph_Class Graph2; // 缩点图
bool InGraph2[MAXN]; // 记录每个原图中的点是否在缩点图中
class Tarjan_Class
{
private:
int low[MAXN], dfn[MAXN], num = 0;
int sccno[MAXN];
std::stack<int> Scc; // 辅助栈
void dfs1(int Now)
{
Scc.push(Now);
low[Now] = dfn[Now] = ++num;
for (int i = Graph1.head[Now]; ~i; i = Graph1.Edge[i].next) {
int NowTo = Graph1.Edge[i].to, NowW = Graph1.Edge[i].w;
/*下降边*/
if (!dfn[NowTo]) {
dfs1(NowTo);
low[Now] = std::min(low[Now], low[NowTo]);
}
/*上升边*/
else if (!sccno[NowTo]){
low[Now] = std::min(low[Now], dfn[NowTo]);
}
}
if (low[Now] == dfn[Now]) {
scc_cnt++;
Graph2.Val[scc_cnt] = 0;
Graph2.End[scc_cnt] = false;
while (!Scc.empty()) {
int NowSCC = Scc.top();
Scc.pop();
sccno[NowSCC] = scc_cnt;
Graph2.Val[scc_cnt] += Graph1.Val[NowSCC];
Graph2.End[scc_cnt] |= Graph1.End[NowSCC];
if (Now == NowSCC) break;
}
}
}
public:
int scc_cnt = 0;
/*tarjan 求缩点图*/
void solve()
{
dfs1(S); // 从市中心开始缩点
Graph2.head_init();
for (int i = 1; i <= N; i++) {
/*标记无意义的点 (市中心出发无法到达)*/
if (dfn[i]) InGraph2[i] = true;
else continue;
int scc_i = sccno[i];
for (int j = Graph1.head[i]; ~j; j = Graph1.Edge[j].next) {
int point_j = Graph1.Edge[j].to;
if (!dfn[point_j]) continue;
int scc_j = sccno[point_j];
if (scc_j == scc_i) continue;
Graph2.addedge(scc_i, scc_j);
Graph2.InDegree[scc_j]++;
}
}
}
} Tarjan;
class Sol_Class
{
protected:
std::queue<int> Q;
int LSY[MAXN], LSY_cnt = 0;
/*topo 排序*/
void topo()
{
for (int i = 1; i <= Tarjan.scc_cnt; i++) {
if (!Graph2.InDegree[i]) Q.push(i);
}
while (!Q.empty())
{
int Now = Q.front();
LSY[++LSY_cnt] = Now;
Q.pop();
for (int i = Graph2.head[Now]; ~i; i = Graph2.Edge[i].next) {
int NowTo = Graph2.Edge[i].to, NowW = Graph2.Edge[i].w;
if (!(--Graph2.InDegree[NowTo])) {
Q.push(NowTo);
}
}
}
}
int dp[MAXN];
public:
/*dp 求最长路*/
void solve()
{
topo();
int Ans = 0;
memset(dp, 0, sizeof(dp));
dp[LSY[1]] = Graph2.Val[LSY[1]];
for (int i = 1; i <= LSY_cnt; i++)
{
int Now = LSY[i];
for (int j = Graph2.head[Now]; ~j; j = Graph2.Edge[j].next) {
int NowTo = Graph2.Edge[j].to;
dp[NowTo] = std::max(dp[NowTo], dp[Now] + Graph2.Val[NowTo]);
}
if (Graph2.End[Now]) Ans = std::max(Ans, dp[Now]);
}
printf("%d", Ans);
}
} Sol;
int main()
{
scanf("%d %d", &N, &M);
Graph1.head_init();
for (int i = 1; i <= M; i++) {
int u, v;
scanf("%d %d", &u, &v);
Graph1.addedge(u, v);
}
for (int i = 1; i <= N; i++)
scanf("%d", &Graph1.Val[i]);
scanf("%d %d", &S, &P);
for (int i = 1; i <= P; i++) {
int u;
scanf("%d", &u);
Graph1.End[u] = true; // 标记酒吧
}
Tarjan.solve();
Sol.solve();
return 0;
}
总结
简单题

浙公网安备 33010602011771号