Codeforces1472 G. Moving to the Capital(记忆化搜索)

本文详细介绍了如何使用记忆化搜索解决有向图中寻找从首都出发,通过特定步数到达各城市的最短路径问题。在Dijkstra算法的基础上,通过d[i][0]和d[i][1]分别记录不用和使用一次特殊走法的最优解,实现了状态转移。最终通过dfs遍历所有节点,找出每个点的最小距离。
摘要由CSDN通过智能技术生成

给定n个点m条边的有向图,
点1为首都,设d(i)为点1到点i的最短路长度。
假设一开始你在点i,你下一步有3种走法:
1.从点i走到点j,同时满足有路且d(i)<d(j)
2.从点i走到点j,同时满足有路且d(i)>=d(j)
3.停止走动

第2种走法最多执行一次,
现在问对于每个点i,走若干次后到达的点s的d(s)最小是多少(即尽量靠近点1)。

数据范围:n,m<=2e5,保证点1可以到达任意城市.

分析:由于终点1是确定的,所以每个点的答案也是确定的,并且更靠近1的点的答案可以转移得出指向它的点的答案。 于是联想到记忆化搜索在回溯的时候转移。

d[i][0]表示点i不用第二种走法的最优解,
d[i][1]表示点i用第二种走法的最优解。

d[1][0]=d[1][1]=dist[1]=0;//边界

d[i][0]=min(dist[i],d[j][0]) (存在i->j  ,dist[i]>dist[j])

d[i][1]=min(dist[i] , d[j][1]) (存在i->j  ,dist[i]>dist[j])
d[i][1]=min(d[i][1] , d[j][0]) (存在i->j,dist[i]<=dist[j])
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e5+5;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+5;
int dist[maxn];//最短路
vector<int> g[maxn];
int n,m;
int d[maxn][2];//0表示不用走法2的答案 1表示可以用1次走法2的答案   记忆化搜索回溯的时候转移
bool vis[maxn];
struct node 
{
    int d,v;
    bool operator < (const node &x)const 
    {
        if(d != x.d)
            return d>x.d;
        return v<x.v;
    }                
}h;
void dijkstra()
{
    priority_queue<node> q;
    q.push({0,1});
    dist[1]=0;
    while(q.size())
    {
        h=q.top(),q.pop();
        if(vis[h.v]) continue;
        vis[h.v]=1;
        for(int i:g[h.v]) 
        {
            if(!vis[i] && dist[i] > h.d+1)
            {
                dist[i]=h.d+1;
                q.push({dist[i],i});
            }
        }
    }
}
int dfs(int v,int f)
{
    if(d[v][f] != -1) return d[v][f];
    d[v][f]=dist[v];//答案的初值
    for(int i:g[v])
    {
        if(dist[i] > dist[v])//这种不会消耗第二种走法的次数 ,所以f==0也可以往下搜
        {
            d[v][f]=min(d[v][f],dfs(i,f));
        }
        else //必须还可以消耗一次第二种走法的次数
        {
            if(f==1)
            {
                d[v][f]=min(d[v][f],dfs(i,0));
            }
        }
    }
    return d[v][f];
}
int main()
{
    #ifndef ONLINE_JUDGE
    // freopen("data.in.txt","r",stdin);
    // freopen("data.out.txt","w",stdout);
    #endif
    int t;cin>>t;
    while(t--)
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            g[i].clear();
            vis[i]=0;
            dist[i]=INF;
            d[i][0]=d[i][1]=-1;
        }
        for(int i=1;i<=m;i++)
        {
            int v,u;
            scanf("%d %d",&v,&u);
            g[v].push_back(u);
        } 
        dijkstra();     
        for(int i=1;i<=n;i++)
        {
            dfs(i,0),dfs(i,1);
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d ",d[i][1]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值