洛谷 P3387 【模板】缩点 tarjan缩点+记忆化搜索

https://www.luogu.org/problem/P3387
题目背景

缩点+DP
题目描述

给定一个n个点m条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。
输入格式

第一行,n,m

第二行,n个整数,依次代表点权

第三至m+2行,每行两个整数u,v,表示u->v有一条有向边
输出格式

共一行,最大的点权之和。
输入输出样例
输入 #1

2 2
1 1
1 2
2 1

输出 #1

2

说明/提示

n<=104,m<=105,0<=点权<=1000

算法:Tarjan缩点+DAGdp

思路:为啥要缩点,因为可以多次经过一条边或者一个点,因此若一个点属于一个环,根据贪心的思想必定要把这个环都走一遍,而 t a r j a n tarjan tarjan缩点后得到的有向无环图上的每个点其实都是一个环。然后我们就考虑在得到的图上记忆化搜索呗,把能走的路径都试一下,记录最大值。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;

const int maxn=1e4+5;
const int maxm=1e5+5;

struct edge
{
    int to,nxt;
}Edge[maxm];

int head_sd[maxn];
edge sd[maxm];
int head[maxn],dfn[maxn],low[maxn],Stack[maxn],ins[maxn],id[maxn];
int ind[maxn],outd[maxn];//入度和出度
int a[maxn],sum[maxn],dp[maxn];
vector<int> scc[maxn];
int n,m,tot,num,top,cnt,tol;

void addedge(int x,int y)
{
    Edge[++tot].to=y,Edge[tot].nxt=head[x],head[x]=tot;
}

void addedge_sd(int x,int y)
{
    sd[++tol].to=y,sd[tol].nxt=head_sd[x],head_sd[x]=tol;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++num;
    Stack[++top]=x,ins[x]=1;
    int y;
    for(int i=head[x];i;i=Edge[i].nxt)
    {
        y=Edge[i].to;
        if(!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if(ins[y])
            low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x])
    {
        ++cnt;
        do
        {
            y=Stack[top--],ins[y]=0;
            id[y]=cnt,scc[cnt].push_back(y);
            sum[cnt]+=a[y];
        }while(x!=y);
    }
}

void work(int u)
{
    if(dp[u])
        return ;
    dp[u]=sum[u];
    int v;
    int MAX=0;
    for(int i=head_sd[u];i;i=sd[i].nxt)
    {
        v=sd[i].to;
        if(!dp[v])
            work(v);
        MAX=max(MAX,dp[v]);
    }
    dp[u]+=MAX;
}

int main()
{
    scanf("%d%d",&n,&m);
    int x,y;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=n;i++)
    {
        for(int j=head[i];j;j=Edge[j].nxt)
        {
            y=Edge[j].to;
            if(id[i]==id[y])
                continue;
            addedge_sd(id[i],id[y]);
        }
    }
    int ans=0;
    for(int i=1;i<=cnt;i++)
        if(!dp[i])
            work(i),ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}

基于python实现的粒子群的VRP(车辆配送路径规划)问题建模求解+源码+项目文档+算法解析,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问题时比较容易实现,而在VRP问题上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。 算法设计的关键在于如何向表现较好的个体学习,标准粒子群算法引入惯性因子w、自我认知因子c1、社会认知因子c2分别作为自身、当代最优解和历史最优解的权重,指导粒子速度和位置的更新,这在求解函数极值问题时比较容易实现,而在VRP问题上,速度位置的更新则难以直接采用加权的方式进行,一个常见的方法是采用基于遗传算法交叉算子的混合型粒子群算法进行求解,这里采用顺序交叉算子,对惯性因子w、自我认知因子c1、社会认知因子c2则以w/(w+c1+c2),c1/(w+c1+c2),c2/(w+c1+c2)的概率接受粒子本身、当前最优解、全局最优解交叉的父代之一(即按概率选择其中一个作为父代,不加权)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值