拓扑排序+dp

题目描述

小明要去一个国家旅游。这个国家有#NN个城市,编号为11至NN,并且有MM条道路连接着,小明准备从其中一个城市出发,并只往东走到城市i停止。

所以他就需要选择最先到达的城市,并制定一条路线以城市i为终点,使得线路上除了第一个城市,每个城市都在路线前一个城市东面,并且满足这个前提下还希望游览的城市尽量多。

现在,你只知道每一条道路所连接的两个城市的相对位置关系,但并不知道所有城市具体的位置。现在对于所有的i,都需要你为小明制定一条路线,并求出以城市ii为终点最多能够游览多少个城市。

输入格式

第11行为两个正整数N, MN,M。

接下来MM行,每行两个正整数x, yx,y,表示了有一条连接城市xx与城市yy的道路,保证了城市xx在城市yy西面。

输出格式

NN行,第ii行包含一个正整数,表示以第ii个城市为终点最多能游览多少个城市。

输入输出样例

输入 #1复制

5 6
1 2
1 3
2 3
2 4
3 4
2 5

输出 #1复制

1
2
3
4
3

说明/提示

均选择从城市1出发可以得到以上答案。

对于20\%20%的数据,N ≤ 100N≤100;

对于60\%60%的数据,N ≤ 1000N≤1000;

对于100\%100%的数据,N ≤ 100000,M ≤ 200000N≤100000,M≤200000。

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=2e5+5;
int head[maxn<<1];
struct node{
    int to,next;
}edge[maxn<<1];
int cnt=0;
void add_edge(int from,int to)
{
    cnt++;
    edge[cnt].to=to;
    edge[cnt].next=head[from];
    head[from]=cnt;
}
int dp[maxn],top[maxn],in[maxn];
int main()
{
    int stop=0;
    int n,m;cin>>n>>m;
    memset(head,-1,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        int from,to;cin>>from>>to;
        add_edge(from,to);
        in[to]++;
    }
    for(int i=1;i<=n;i++) {
        if(in[i]==0) dp[i]=1,top[++stop]=i;
    }
    for(int i=1;i<=stop;i++)
    {
        int now=top[i];
        for(int j=head[now];j!=-1;j=edge[j].next)
        {
            int to=edge[j].to;
            in[to]--;
            if(in[to]==0) top[++stop]=to;
        }
    }
    for(int i=1;i<=stop;i++)
    {
        int now=top[i];
        for(int j=head[now];j!=-1;j=edge[j].next)
        {
            int to=edge[j].to;
            dp[to]=max(dp[to],dp[now]+1);
        }
    }
    for(int i=1;i<=n;i++) cout<<dp[i]<<endl;
}

题目描述

CC国有nn个大城市和mm 条道路,每条道路连接这 nn个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 mm 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为 11条。

CC国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。

商人阿龙来到 CC 国旅游。当他得知同一种商品在不同城市的价格可能会不同这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚回一点旅费。设 CC 国 n 个城市的标号从 1~ n1 n,阿龙决定从 11号城市出发,并最终在 nn 号城市结束自己的旅行。在旅游的过程中,任何城市可以重复经过多次,但不要求经过所有 nn 个城市。阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品――水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。由于阿龙主要是来 CC 国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。

假设 CC国有 55个大城市,城市的编号和道路连接情况如下图,单向箭头表示这条道路为单向通行,双向箭头表示这条道路为双向通行。

假设 1~n1 n 号城市的水晶球价格分别为 4,3,5,6,14,3,5,6,1。

阿龙可以选择如下一条线路:11->22->33->55,并在 22号城市以33 的价格买入水晶球,在 33号城市以55的价格卖出水晶球,赚取的旅费数为 2。

阿龙也可以选择如下一条线路11->44->55->44->55,并在第11次到达55 号城市时以 11的价格买入水晶球,在第 22 次到达44 号城市时以66 的价格卖出水晶球,赚取的旅费数为55。

现在给出 nn个城市的水晶球价格,mm 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。

输入格式

第一行包含 22 个正整数nn和 mm,中间用一个空格隔开,分别表示城市的数目和道路的数目。

第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这 n 个城市的商品价格。

接下来 mm 行,每行有33个正整数x,y,zx,y,z,每两个整数之间用一个空格隔开。如果 z=1z=1,表示这条道路是城市xx到城市yy之间的单向道路;如果z=2z=2,表示这条道路为城市 xx和城市yy之间的双向道路。

输出格式

一 个整数,表示最多能赚取的旅费。如果没有进行贸易,则输出 00。

输入输出样例

输入 #1复制

5 5 
4 3 5 6 1 
1 2 1 
1 4 1 
2 3 2 
3 5 1 
4 5 2 

输出 #1复制

5

说明/提示

【数据范围】

输入数据保证 11 号城市可以到达nn号城市。

对于 10%的数据,1≤n≤61≤n≤6。

对于 30%的数据,1≤n≤1001≤n≤100。

对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。

对于 100%的数据,1≤n≤1000001≤n≤100000,1≤m≤5000001≤m≤500000,1≤x1≤x,y≤ny≤n,1≤z≤21≤z≤2,1≤1≤各城市

水晶球价格≤100≤100。

NOIP 2009 提高组 第三题

#include<iostream>
#include<cstring>
using namespace std;
//#define int long long
const int maxn=5e5+5;
int n,m;
int head1[maxn<<1],head2[maxn<<1];
int low[maxn],dfn[maxn],st[maxn],id,instack[maxn],belong[maxn],dp[maxn],mx[maxn],mn[maxn],mc[maxn],top[maxn],in[maxn];
int val[maxn];
struct node{
    int to,next;
}edge1[maxn<<1],edge2[maxn<<1];
int cnt1,cnt2;
void init()
{
    memset(head1,-1,sizeof(head1));
    memset(head2,-1,sizeof(head2));
    memset(dfn,-1,sizeof(dfn));
}
void add_edge1(int from,int to)
{
    cnt1++;
    edge1[cnt1].to=to;
    edge1[cnt1].next=head1[from];
    head1[from]=cnt1;
}
void add_edge2(int from,int to)
{
    cnt2++;
    edge2[cnt2].to=to;
    edge2[cnt2].next=head2[from];
    head2[from]=cnt2;
}
int num,cnt;
void dfs(int root)
{
    low[root]=dfn[root]=++id;
    st[++num]=root;
    instack[root]=1;
    for(int i=head1[root];i!=-1;i=edge1[i].next)
    {
        int now=edge1[i].to;
        if(dfn[now]==-1)
        {
            dfs(now);
            low[root]=min(low[root],low[now]);
        }
        else if(instack[now]) low[root]=min(low[root],dfn[now]);
    }
    if(low[root]==dfn[root])
    {
        cnt++;
        int Mx=0,Mn=105;
        while(st[num]!=root)
        {
            belong[st[num]]=cnt;
            instack[st[num]]=0;
            Mx=max(Mx,val[st[num]]);
            Mn=min(Mn,val[st[num]]);
            num--;
        }
        instack[st[num]]=0;
        belong[st[num]]=cnt;
        Mx=max(Mx,val[st[num]]);
        Mn=min(Mn,val[st[num]]);
        num--;
        mx[cnt]=Mx,mn[cnt]=Mn;
    }
}
void trajan()
{
    for(int i=1;i<=n;i++) if(dfn[i]==-1) dfs(i);
    for(int i=1;i<=n;i++)
    {
        for(int j=head1[i];j!=-1;j=edge1[j].next)
        {
            int to=edge1[j].to;
            if(belong[i]!=belong[to]) {
                add_edge2(belong[i],belong[to]);
            }
        }
    }
    for(int i=1;i<=cnt;i++)
    {
        for(int j=head2[i];j!=-1;j=edge2[j].next)
        {
            int to=edge2[j].to; in[to]++;
        }
    }
    int stop=0;
    for(int i=1;i<=cnt;i++) {
        if(in[i]==0) top[++stop]=i;
    }
    for(int i=1;i<=stop;i++)
    {
        for(int j=head2[top[i]];j!=-1;j=edge2[j].next)
        {
            int to=edge2[j].to;
            in[to]--;
            if(in[to]==0) top[++stop]=to;
        }
    }
    mc[top[1]]=mn[top[1]];
    for(int i=1;i<=stop;i++)
    {
        int now=top[i];
        for(int j=head2[now];j!=-1;j=edge2[j].next)
        {
            int to=edge2[j].to;
            mc[to]=min(mc[now],mn[to]);
            dp[to]=max(max(dp[to],dp[now]),max(mx[to]-mc[to],mx[to]-mn[to]));
        }
    }
    cout<<dp[belong[n]];
}
int main()
{
    init();
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>val[i];
    for(int i=1;i<=m;i++)
    {
        int from,to;cin>>from>>to;
        int op;cin>>op;
        add_edge1(from,to);
        if(op==2) add_edge1(to,from);
    }
    trajan();
    return 0;

Link hates running.

Today, Link is asked to run. Roads in BIT can be described as nn nodes and mm directed edges. Link has to run from node 11 to node nn. When Link is at node u_iui​, he can run through the ii-th edge to get to node v_ivi​. Every time he runs through the ii-th edge, he spends e_iei​ points of energy and gains p_ipi​ points of physical fitness.

As a lazy boy, Link wants to spend as little energy as possible. He is also greedy and wants to gain maximum physical fitness when spending minimum energy.

Tell Link the minimum energy min_emine​ he needs to spend and the maximum physical fitness max_pmaxp​ he can gain when spending the minimum energy.

Input

Each test contains multiple test cases. The first line contains the number of test cases T(1 \le T \le 12)T(1≤T≤12). Description of the test cases follows.

The first line contains two integers n,m(2 \leq n \leq 10^5, 1 \leq m \leq 3 \times 10^5)n,m(2≤n≤105,1≤m≤3×105), which are the number of nodes and the number of edges.

Each of the next mm lines contains four integers u_i,v_i,e_i,p_i(1 \leq u_i,v_i \leq n, 0 \leq e_i,p_i \leq 10^9ui​,vi​,ei​,pi​(1≤ui​,vi​≤n,0≤ei​,pi​≤109), describing an edge.

Output

For each test case, output min_emine​ and max_pmaxp​ in a single line, separated by one space.

It is guaranteed that THE ANSWER EXISTS and CAN BE REPRESENTED AS A NUMBER.

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<ll,int> PII;
const int N=1e5+5,M=6e5+5;
const ll INF=1e18;
vector<int> v[N];
int h[N],e[M],ne[M],w[M],p[M],idx,h2[N];
int n,m;
struct node{
	int x,y,z,d;
}edge[M];
void add(int h[],int a,int b,int c,int d)
{
	e[idx]=b,w[idx]=c,p[idx]=d,ne[idx]=h[a],h[a]=idx++;
}
int st[N],instack[N],dfn[N],low[N],belong[N],top,scc_cnt,id;
void tarjan(int root)
{
	low[root]=dfn[root]=++id;
	instack[root]=1;
	st[++top]=root;
	for(int i=h[root];~i;i=ne[i])
	{
		int to=e[i];
		if(!dfn[to])
		{
			tarjan(to);
			low[root]=min(low[to],low[root]);
		}
		else if(instack[to]) low[root]=min(low[root],dfn[to]);
	}
	if(dfn[root]==low[root])
	{
		int y;
		scc_cnt++;
		do{
			y=st[top--];
			v[scc_cnt].push_back(y);
			belong[y]=scc_cnt;
			instack[root]=0;
		}while(y!=root);
	}
}
int du[N],qu[N];
ll dp[N],g[N];
int vis[N];
void djk()
{
	dp[1]=0;
	priority_queue<PII,vector<PII>,greater<PII> > q;
	q.push({0,1});
	while(!q.empty())
	{
		PII now=q.top();q.pop();
		int v=now.second;
		if(vis[v]) continue;
		vis[v]=1;
		for(int i=h[v];~i;i=ne[i])
		{
			int to=e[i];
			if(dp[to]>dp[v]+w[i]) 
			{
				dp[to]=dp[v]+w[i];
				q.push({dp[to],to});
			}
		}
	}
}
void topsort(int h[])
{
	int hh=0,tt=-1;
	for(int i=1;i<=n;i++)
	{
		if(belong[1]==belong[i]) qu[++tt]=i;
	}
	while(hh<=tt)
	{
		int t=qu[hh++];
		for(int i=h[t];~i;i=ne[i])
		{
			int to=e[i];
			if(belong[to]==belong[t]) continue;
			g[belong[to]]=max(g[belong[t]]+p[i],g[belong[to]]);
			du[belong[to]]--;
			if(du[belong[to]]==0) 
			{
				for(int j=0;j<v[belong[to]].size();j++)
				{
					qu[++tt]=v[belong[to]][j];
				}
			}
		}
	}
}
void solve()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;i++) 
	{
		dp[i]=INF,g[i]=0;
		h[i]=-1;dfn[i]=0;
		h2[i]=-1;du[i]=0;
		vis[i]=0;instack[i]=0;
		v[i].clear();
	}
	scc_cnt=top=id=idx=0;
	for(int i=1;i<=m;i++)
	{
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		edge[i]={a,b,c,d};
		add(h,a,b,c,d);
	}
	djk();
	for(int i=1;i<=m;i++)
	{
		int a=edge[i].x,b=edge[i].y,c=edge[i].z,d=edge[i].d;
		if(dp[b]==dp[a]+c)
		add(h2,a,b,c,d),du[b]++;
	}
	for(int i=1;i<=n;i++)
	{
		if(!dfn[i]) tarjan(i);
	}
	topsort(h2);
	printf("%lld %lld\n",dp[n],g[belong[n]]);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		solve();
	}
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值