倍增思路dp yyds

本文探讨了一种解决开车旅行问题的方法,利用动态规划记录所有可能的路径,并通过二进制优化和多集合访问来处理路径选择。在处理过程中,使用了 multiset 数据结构进行路径优化。文章还展示了具体的 C++ 代码实现,包括 dp 数组、路径优化和搜索策略。最后,程序用于寻找给定费用下最优路径的选择并输出路径长度。
摘要由CSDN通过智能技术生成

[NOIP2012 提高组] 开车旅行 - 洛谷

1.首先确定人及地点可以确定路径

2.所以对应多种情况访问,我们就用dp记录所有情况,因为换人和路径都可用dp处理

3.路径大采取倍增的方式,对于固定的路径长采用二进制的方式访问(log优化)

4.multiset可用于重复 元素自动排序 访问需要i++ i--(log)

5.有序存贮访问全部可以达到单向动态记录

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+50;
const int Inf = 2e9;
struct one{
    int h,id;
    friend bool operator < (one a,one b)
    {
        
        return a.h<b.h;
    }
};
ll f[18][100005][2],da[18][100005][2],db[18][100005][2];
multiset<one>s;
multiset<one>::iterator it;
int n,m,u,p,x0,h[100005];
one kk[5];
ll la,lb;
bool cmp(one x,one y)
{
    if(x.h==y.h)
        return h[x.id]<h[y.id];
    return x.h<y.h;
}
void solve(int ss,int x)
{
    la=0,lb=0;
    for(int i=17;i>=0;--i)
    {
        if(f[i][ss][0]&&la+lb+da[i][ss][0]+db[i][ss][0]<=x)
        {
            
            la+=da[i][ss][0];
            lb+=db[i][ss][0];
            ss=f[i][ss][0];
        }
    }
    return ;
}
int main ()
{
    scanf("%d",&n);
    one st;
    for(int i=1;i<=4;++i)
    {
        st.id=0;
        if(i>2)
        {
            st.h=Inf;
        }
        else st.h=-Inf;
        s.insert(st);
    }
    for(int i=1;i<=n;++i)
        scanf("%d",&h[i]);
    for(int i=n;i>=1;--i)
    {
        st=(one){h[i],i};
        s.insert(st);
        it=s.lower_bound(st);
        for(int j=1;j<=2;++j)
        {
            it++;
            kk[j].id=(*it).id;
            if(kk[j].id)
            kk[j].h=abs((*it).h-h[i]);
            else kk[j].h=Inf;
        }
        it--;
        it--;
        for(int j=3;j<=4;++j)
        {
            it--;
            kk[j].id=(*it).id;
            if(kk[j].id)
            kk[j].h=abs((*it).h-h[i]);
            else kk[j].h=Inf;
        }
        sort(kk+1,kk+5,cmp);
        f[0][i][1]=kk[1].id;
        db[0][i][1]=kk[1].h;
        f[0][i][0]=kk[2].id;
        da[0][i][0]=kk[2].h;
    }
    for(int k=1;k<=17;++k)
        for(int i=1;i<=n;++i)
            for(int j=0;j<2;++j)
            {
                if(k==1)
                {
                    f[1][i][j]=f[0][f[0][i][j]][1-j];
                    da[1][i][j]=da[0][i][j]+da[0][f[0][i][j]][1-j];
                    db[1][i][j]=db[0][i][j]+db[0][f[0][i][j]][1-j];
                }
                else 
                {
                    f[k][i][j]=f[k-1][f[k-1][i][j]][j];
                    da[k][i][j]=da[k-1][i][j]+da[k-1][f[k-1][i][j]][j];
                    db[k][i][j]=db[k-1][i][j]+db[k-1][f[k-1][i][j]][j];
                }
            }
    scanf("%d",&x0);
    scanf("%d",&m);
    double ans=(double)Inf,nowans;
    p=-1;
    for(int i=1;i<=n;++i)
    {
        solve(i,x0);
        if(lb==0)
        {
            if(p==-1)
                p=i;
        }
        else 
        {
            nowans=(double)la/(double)lb;
            if(nowans==ans)
            {
                if(h[i]>h[p])
                    p=i;
            }
            else if(nowans<ans)
            {
                p=i;
                ans=nowans;
            }
            
        }
    }
    printf("%d\n",p);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&p,&x0);
        solve(p,x0);
        printf("%d %d\n",la,lb);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值