Codeforces 1486 E. Paired Payment —— dijkstra,dp

15 篇文章 0 订阅

This way

题意:

一张大小为n的图,m条边,每次你需要连续走两条边,花费为 ( w i + w j ) 2 (wi+wj)^2 (wi+wj)2,问你从1走到i(1<=i<=n)的最小花费。

题解:

很明显是dijkstra,但是它每次要走两条边,我们不能枚举儿子的儿子,因为这必然会TLE,所以我们需要存两个状态:走完了和走了一半。那么我们怎么比较走了一半的花费谁优谁劣呢,我们可以看到w的范围只有50,似乎在暗示着什么。那么就设
dp[i][j][k]表示到了第i个点,j:走完还是走了一半,k:如果走了一半,前一条边的花费是多少。
那么就可以转移了:
在这里插入图片描述
表示当前走完了(我们用0表示走完),并且这不是赘余状态。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
ll dp[N][2][55];
struct edge{
    int to,next,v;
}e[N*2];
int cnt,head[N];
void add(int x,int y,int v){
    e[cnt].to=y;
    e[cnt].next=head[x];
    e[cnt].v=v;
    head[x]=cnt++;
}
struct node{
    int x,ad;
    ll v;
    bool operator< (const node& a)const {
        return v>a.v;
    }
};
priority_queue<node>Q;
void dij(){
    Q.push({1,0,0});
    while(!Q.empty()){
        node u=Q.top();Q.pop();
        for(int i=head[u.x];~i;i=e[i].next){
            int x=u.x,ne=e[i].to,v=e[i].v,ad=u.ad;
            if(!ad&&dp[x][0][0]==u.v){
                if(dp[ne][1][v]==-1||dp[ne][1][v]>dp[x][0][0])
                    dp[ne][1][v]=dp[x][0][0],Q.push({ne,v,u.v});
            }
            if(ad&&dp[x][1][ad]==u.v){
                if(dp[ne][0][0]==-1||dp[ne][0][0]>dp[x][1][ad]+(ad+v)*(ad+v))
                    dp[ne][0][0]=dp[x][1][ad]+(ad+v)*(ad+v),Q.push({ne,0,dp[ne][0][0]});
            }
        }
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    int n,m,x,y,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&x,&y,&v),add(x,y,v),add(y,x,v);
    memset(dp,-1,sizeof(dp));
    dp[1][0][0]=0;
    dij();
    for(int i=1;i<=n;i++)
        printf("%lld ",dp[i][0][0]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值