CF-721C DAG图拓扑排序+费用DP

比赛的时候写了个记忆化搜索,超时了。

后来学习了一下,这种题目应该用拓扑排序+DP来做。

dp[][]保存走到[第i个节点][走过j个点]时所用的最短时间。

pre[][]用前驱节点求路径

然后遍历一下dp[n][],求满足t范围的最大下标。

#include <iostream>
#include <queue>
#include <cstdio>
#include <vector>
#include <stack>
#define N 5050
#define INF 1000000200
//#define LL long long int
using namespace std;
int n,m,sum,cnt,flag,t;
int deg[N];
vector<int> g[N],f[N];//保存后继节点
int dp[N][N],pre[N][N];//保存走到第i个点,走过j个点时所用时间
struct cmp
{
    bool operator()(int a,int b)
    {
        return a>b;
    }
};
void topoSort()
{
    priority_queue<int,vector<int>,cmp> q;
    for(int i=1;i<=n;i++)
        if(deg[i]==0)
            q.push(i),deg[i]--;
    while(!q.empty())
    {
        //if(q.size()>1) 可用于判断是否充分排序。
        //如果有多个入度为0的同时入队,说明他们之间没有明确的排序条件。
        int u=q.top();
        q.pop();
        sum--;//可用于判断是否有冲突,如果有冲突,就会导致两者或者已上的节点入度无法降为0
        for(int i=0;i<g[u].size();i++)
        {
            int e=g[u][i];
            deg[e]--;
            for(int j=2;j<=n;j++)
            {
                //cout<<dp[u][i]<<‘ ‘<<e<<endl;
                if(dp[e][j]>dp[u][j-1]+f[u][i])
                    dp[e][j]=dp[u][j-1]+f[u][i],pre[e][j]=u;
            }
        }
        for(int i=1;i<=n;i++)
            if(deg[i]==0)
                q.push(i),deg[i]--;
    }
}
void ini()
{
    for(int i=0;i<=n;i++)
        deg[i]=0,flag=0,g[i].clear(),f[i].clear();
    cnt=0,sum=n;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            dp[i][j]=t+100,pre[i][j]=-1;
    dp[1][1]=0;
}
void add(int u,int v,int fe)
{
    deg[v]++;
    g[u].push_back(v);
    f[u].push_back(fe);
}
int main() {
    //cin.sync_with_stdio(false);
    scanf("%d%d%d",&n,&m,&t);
        ini();
        for(int i=0;i<m;i++)
        {
            int u,v,fe;
            scanf("%d%d%d",&u,&v,&fe);
            add(u,v,fe);
        }
        //cout<<dp[1][2]<<endl;
        topoSort();
        int Maxp=-1;
        for(int i=1;i<=n;i++)
        {
            if(dp[n][i]<=t)
                Maxp=i;//求最大合法经过点数
        }
        stack<int> ss;
        int pos=n,lef=Maxp;
        cout<<Maxp<<endl;
        while(pos!=-1)
        {
            ss.push(pos);
            pos=pre[pos][lef],lef--;
        }
        //ss.push(1);
        while(ss.size()>1)
            cout<<ss.top()<<‘ ‘,ss.pop();
        cout<<ss.top()<<endl;
        ss.pop();

    return 0;
}
时间: 2024-08-07 08:39:36

CF-721C DAG图拓扑排序+费用DP的相关文章

数据结构:图--拓扑排序

拓扑排序 拓扑排序 在实际应用中,有向图的边可以看做是顶点之间制约关系的描述.把顶点看作是一个个任务,则对于有向边<Vi,Vj>表明任务Vj的完成需等到任务Vi完成之后,也就是说任务Vi先于任务Vj完成.对于一个有向图,找出一个顶点序列,且序列满足:若顶点Vi和Vj之间有一条边<Vi,Vj>,则在此序列中顶点Vi必在顶点Vj之前.这样的一个序列就称为有向图的拓扑序列(topological order). 步骤 从有向图中选取一个没有前驱(入度为0)的顶点输出. 删除图中所有以它为

HDU4857——逃生(反向建图+拓扑排序)

逃生 Description 糟糕的事情发生啦,现在大家都忙着逃命.但是逃命的通道很窄,大家只能排成一行. 现在有n个人,从1标号到n.同时有一些奇怪的约束条件,每个都形如:a必须在b之前.同时,社会是不平等的,这些人有的穷有的富.1号最富,2号第二富,以此类推.有钱人就贿赂负责人,所以他们有一些好处.负责人现在可以安排大家排队的顺序,由于收了好处,所以他要让1号尽量靠前,如果此时还有多种情况,就再让2号尽量靠前,如果还有多种情况,就让3号尽量靠前,以此类推.那么你就要安排大家的顺序.我们保证一

[HAOI2012] 道路 - 最短路图,拓扑排序,dp

给定一个无向图,一条路径被称为最短路,当且仅当不存在从它的起点到终点的另外一条路径总长度比它小.两条最短路不同,当且仅当它们包含的道路序列不同.我们需要对每条道路的重要性进行评估,评估方式为计算有多少条不同的最短路经过该道路.\(n\leq 1500,m\leq 5000,w\leq 10000\) Solution 枚举每个点作为起点,跑出最短路径图,对其拓扑排序后,DP 统计出每个点发出和从起点到达该点的最短路的总数,然后扫一遍所有边统计答案即可. 注意可能有重边 #include <bit

算法系列之图--拓扑排序

本文介绍使用深度先搜索对向无环图(DAG)进行拓扑排序. 对于一个有向无环图G=(V,E)来说,其拓扑排序是G中所有结点的一种线性次序,该次序满足如下条件:如果G包含边(u,v)则结点u在拓扑排序中处于结点v的前面(若图G包含一个环路则不可能排出一个线性次序).可将图中的拓扑排序看成是将图的所有结点在一条水平线上排开,图中所有边都从左指向右. 给一个拓扑图如下示: 拓扑排序算法与DFS相似,但是在拓扑排序的过程中,每个结点都是后与其临接链表里的结点而放入Stack中. 具体代码如下示: 1 #i

[POI2015][bzoj4383] Pustynia [线段树优化建图+拓扑排序]

题面 bzoj权限题传送门 luogu传送门 思路 首先,这个题目显然可以从所有小的点往大的连边,然后如果没环就一定可行,从起点(入读为0)开始构造就好了 但是问题来了,如果每个都连的话,本题中边数是$O(n^2)$级别的,显然会挂 发现两条性质: 1.所有的限制条件中,给定的总点数不超过3e5个 2.是一个点比一段区间大 第二个条件决定了我们可以利用线段树优化建图,而第一个条件告诉了我们,本题的总边数应该是$sumk\astlog_2n$级别的 那么就做完了 注意拓扑排序的时候有个技巧,把连向

图-&gt;有向无环图-&gt;拓扑排序

文字描述 关于有向无环图的基础定义: 一个无环的有向图称为有向无环图,简称DAG图(directed acycline graph).DAG图是一类较有向树更一般的特殊有向图. 举个例子说明有向无环图的应用.假如有一个表达式: ((a+b)*(b*(c+d))+(c+d)*e)*((c+d)*e), 可以用之前讨论的二叉树来表示,也可以用有向无环图来表示,如下图.显然有向无环图实现了对相同子式的共享,从而比二叉树更节省空间. 关于拓扑排序的基础定义: 由某个集合上的一个偏序得到该集合上的一个全须

图论基础——邻接链表存图+拓扑排序

邻接链表存图,在这里其实是用数组进行模拟的 又叫做链式存储法,本来是要用链表实现的,但大多数情况下只需要用数组模拟即可 例: u(边的起点) v(边的终点) w(边的权值) 4 2 1 1 2 3 1 4 1 1 5 2 4 3 4 2 3 1 话不多说,直接上代码 for(int i=1;i<=m;i++) { scanf("%d%d%d",&u1,&v1,&w1); e[i].u =u1;//赋给第i条边的起点 e[i].v =v1;//赋给第i条边的

【Luogu】P3116会议时间(拓扑排序,DP)

题目链接 本题使用拓扑排序来规划DP顺序.设s[i][j]表示i步是否能走到j这个点,e[i][j]表示i步是否能走到j这个点——用第二条路径.因为要满足无后效性和正确性,只有第i个点已经全部更新完毕的时候才能用它来更新其他的点.所以用拓扑. 代码如下 #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> using namespace std; inline long

CF 274D Lovely Matrix 拓扑排序,缩点 难度:2

http://codeforces.com/problemset/problem/274/D 这道题解题思路: 对每一行统计,以小值列作为弧尾,大值列作为弧头,(-1除外,不连弧),对得到的图做拓扑排序即可. 但本题数据较大,所以需要进行缩点,把相同数值的点缩在一起,成为一个新的大点,原先的小值列向大点连接,再由大点向大值列连接,可以减少边数 举例来说,原本取值为1的有4个点,取值为2的有5个点, 不缩点,就需要20条边 缩点,只需要4+1+5=10条边 (不过我还是觉得这个方法有点投机取巧??