zoukankan      html  css  js  c++  java
  • [NOIP2012] 开车旅行

    戳我
    做了快一天了。。。qwq
    这个题写着真的难受。。。。上午打了快两个小时的暴力(天啊。。我竟然写了这么久)拿到了70分。。。
    这个是我的暴力代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #define MAXN 1010
    #define MAXM 10010
    using namespace std;
    int n,x0,cnt,m;
    int s[MAXM],x[MAXM],ansa[MAXM],ansb[MAXM],ans[MAXM],dist[MAXN][MAXN],aa[MAXM],bb[MAXM];
    struct Node{int id,h,pos1=0,pos2=0;}node[MAXN];
    bool cmp(struct Node x,struct Node y)
    {
        if(x.h<y.h) return 1;
        else return 0;
    }
    inline void solve(int now,int check,int disa,int disb,int limit)
    {
        //printf("now=%d check=%d disa=%d disb=%d limit=%d
    ",now,check,disa,disb,limit);
        if(check==1) //it's time for pos2
        {
            int to=node[now].pos2;
        //	printf("A:to=%d dist=%d
    ",to,dist[now][to]);
            if(to==0||dist[now][to]+disa+disb>limit) 
            {
                ansa[++cnt]=disa;
                ansb[cnt]=disb;
                return;
            }
            solve(to,check^1,disa+dist[now][to],disb,limit);
        }
        else//it's time for pos1
        {
            int to=node[now].pos1;
        //	printf("B:to=%d dist=%d
    ",to,dist[now][to]);
            if(to==0||dist[now][to]+disa+disb>limit)
            {
                ansa[++cnt]=disa;
                ansb[cnt]=disb;
                return;
            }
            solve(to,check^1,disa,disb+dist[now][to],limit);
        }
    }
    int main()
    {
        //freopen("ce.in","r",stdin);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&node[i].h);
        scanf("%d%d",&x0,&m);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&s[i],&x[i]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dist[i][j]=dist[j][i]=abs(node[i].h-node[j].h);
        //check out the distance betweeen two points
        for(int i=1;i<=n;i++)
        {
            int minn1=2147483647,minn2=2147483647;
            //cout<<i<<endl;
            for(int j=i+1;j<=n;j++)
            {
                if(dist[i][j]<minn1||(dist[i][j]==minn1&&node[j].h<node[node[i].pos1].h))
                {
                    minn2=minn1,node[i].pos2=node[i].pos1;
                    minn1=dist[i][j],node[i].pos1=j;
                }
                else if(dist[i][j]<minn2||(dist[i][j]==minn2&&node[j].h<node[node[i].pos2].h))
                    minn2=dist[i][j],node[i].pos2=j;
                //printf("j=%d minn1=%d pos1=%d minn2=%d pos2=%d
    ",j,minn1,node[i].pos1,minn2,node[i].pos2);
            }
        }//find out the position of the minn1 and minn2
        //cout<<endl;
        //for(int i=1;i<=n;i++)
        //	printf("i=%d pos1=%d pos2=%d
    ",i,node[i].pos1,node[i].pos2);
        for(int i=1;i<=m;i++)
            solve(s[i],1,0,0,x[i]);
        memcpy(aa,ansa,sizeof(ansa));
        memcpy(bb,ansb,sizeof(ansb));
        memset(ansa,0,sizeof(ansa));
        memset(ansb,0,sizeof(ansb));
        cnt=0;
        for(int i=1;i<=n;i++)
            solve(i,1,0,0,x0);
        int pos_ans,cur_h;
        double min_ans=1e10;
        //cout<<"cnt="<<cnt<<endl;
        for(int i=1;i<=cnt;i++)
        {
            if(ansb[i]==0) continue;
            //printf("from=%d %.6lf
    ",i,(double)ansa[i]/ansb[i]);
            if((double)ansa[i]/ansb[i]<min_ans)
                min_ans=(double)ansa[i]/ansb[i],pos_ans=i,cur_h=node[i].h;
            else if((double)ansa[i]/ansb[i]==min_ans&&node[i].h>cur_h)
                pos_ans=i,cur_h=node[i].h;
                //printf("pos_ans=%d
    
    ",pos_ans);
        }
        printf("%d
    ",pos_ans);
        for(int i=1;i<=m;i++)
            printf("%d %d
    ",aa[i],bb[i]);
        return 0;
    }
    

    其他的点又MLE又TLE的,反正是GG了。

    所以我们要考虑优化。。。。。什么优化?一看数据范围1e5。。。那么考虑O(nlogn)的做法——自然是倍增了。

    在经过仔细思考之后在看了题解之后——

    我们可以注意到A和B交替开车,就可以将他们两个各开一次的看作一次,状态显然可以推移合并。

    这个题难在预处理。。。。我们需要找每个点的最近城市和次近城市。这个找前驱后继的自然是可以排序(+离散化)之后用双向链表做。。但是蒟蒻我不太会,所以我们可以选择插入,查询复杂度为O(logn)set+lower_bound来寻找。。。。

    看一个daolao用了multiset,开始重复插入四个极值来避免寻找的时候出界导致RE,感觉是个不错的做法·,就学习了一下。

    我们设(f[i][j][0(A)/1(B)])为A/B从第i个城市出发,走(2^j)天到达的城市(注意A,B交替开车)

    然后(sum_a[i][j][0(A)/1(B)])为A/B从第i个城市出发,走(2^j)天,其中A走的路程。

    (sum_b[i][j][0(A)/1(B)])为A/B从第i个城市出发,走(2^j)天,其中B走的路程。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<set>
    #define MAXN 100010
    using namespace std;
    
    int n,x0,m;
    int si[MAXN],xi[MAXN],height[MAXN],f[MAXN][22][2],sum_a[MAXN][22][2],sum_b[MAXN][22][2];
    struct Node{
        int id,h;
        friend bool operator<(Node x,Node y){
            return x.h<y.h;
        }
    };
    multiset<Node>s;
    
    inline void solve(int S,int &dist_a,int &dist_b,int limit)
    {
        int now=S;
        for(int i=20;i>=0;i--)
        {
            if(f[now][i][0]&&sum_a[now][i][0]+sum_b[now][i][0]+dist_a+dist_b<=limit)
            {
                dist_a+=sum_a[now][i][0];
                dist_b+=sum_b[now][i][0];
                now=f[now][i][0];
            }
        }
    }
    
    inline void init()
    {
        for(int i=n;i>=1;i--)
        {
            int to_a,to_b,nxt_pos,nxt_height,pre_pos,pre_height;
            Node cur;
            cur.id=i; cur.h=height[i];
            s.insert(cur);
            multiset<Node>::iterator it=s.lower_bound(cur);
            it++;
            nxt_pos=(*it).id; nxt_height=(*it).h;
            it--,it--;
            pre_pos=(*it).id; pre_height=(*it).h;
            it++;
            //因为我们已经排序过了(set自带从小到大排序功能,我们也在结构体里面定义过了)
            //易知当前节点的最近和次近城市就在他的-2,-1,+1,+2的地方qwq
            //所以我们只需要处理判断这四个点就可以了qwq
            if(abs(nxt_height-height[i])<abs(pre_height-height[i]))
            {
                it++,it++;
                to_b=nxt_pos;
                if(abs(pre_height-height[i])>abs((*it).h-height[i])) to_a=(*it).id;
                else to_a=pre_pos;
            }
            else
            {
                it--,it--;
                to_b=pre_pos;
                if(abs(nxt_height-height[i])>=abs((*it).h-height[i])) to_a=(*it).id;
                else to_a=nxt_pos;
            }
            //这里需要注意等于号的使用,考虑到我们已经按照高度排序过了,所以有的等于号需要加,有的不能加
            f[i][0][0]=to_a; 
            f[i][0][1]=to_b;
            sum_a[i][0][0]=abs(height[to_a]-height[i]);
            sum_b[i][0][0]=0;
            sum_b[i][0][1]=abs(height[to_b]-height[i]);
            sum_a[i][0][1]=0;
        }
        
        for(int i=1;i<=n;i++)
        {
            f[i][1][0]=f[f[i][0][0]][0][1];
            f[i][1][1]=f[f[i][0][1]][0][0];
            sum_a[i][1][0]=sum_a[i][0][0];
            sum_b[i][1][1]=sum_b[i][0][1];
            sum_a[i][1][1]=abs(height[f[i][1][1]]-height[f[i][0][1]]);
            sum_b[i][1][0]=abs(height[f[i][1][0]]-height[f[i][0][0]]); 
        }
        //我们前面处理的是只走了一天的情况,但是因为我们要按照A,B两人交替开车一天视为一次来进行倍增
        //所以我们也需要把走了2^1天的情况处理出来
        for(int k=2;k<=20;k++)
        {
            for(int i=1;i<=n;i++)
            {
                f[i][k][0]=f[f[i][k-1][0]][k-1][0];
                f[i][k][1]=f[f[i][k-1][1]][k-1][1];
                sum_a[i][k][0]=sum_a[i][k-1][0]+sum_a[f[i][k-1][0]][k-1][0];
                sum_b[i][k][0]=sum_b[i][k-1][0]+sum_b[f[i][k-1][0]][k-1][0];
                sum_a[i][k][1]=sum_a[i][k-1][1]+sum_a[f[i][k-1][1]][k-1][1];
                sum_b[i][k][1]=sum_b[i][k-1][1]+sum_b[f[i][k-1][1]][k-1][1];
            }
        }
    }
    
    int main()
    {
        
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&height[i]);
        height[0]=2147483627; height[n+1]=-2147483627;
        Node cur1;
        cur1.h=2147483627;
        cur1.id=0;
        Node cur2;
        cur2.h=-2147483627;
        cur2.id=n+1;
        s.insert(cur1),s.insert(cur1);
        s.insert(cur2),s.insert(cur2);
        //这个就是上面提到的小技巧
        init();
        scanf("%d%d",&x0,&m);
        int cur_ans=0;
        double ans=1e15;
        for(int i=1;i<=n;i++)
        {
            int dist_a=0,dist_b=0;
            solve(i,dist_a,dist_b,x0);
            if(dist_b==0)
            {
                if(ans>1e14) cur_ans=i,ans=1e14;
                else if(ans==1e14&&height[i]>height[cur_ans]) cur_ans=i;
            }
            else
            {
                double kkk=(double)dist_a/dist_b;
                if(kkk<ans) ans=kkk,cur_ans=i;
                else if(kkk==ans&&height[i]>height[cur_ans]) cur_ans=i;
            }
        }
        printf("%d
    ",cur_ans);
        
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&si[i],&xi[i]);
            int dist_a=0,dist_b=0;
            solve(si[i],dist_a,dist_b,xi[i]);
            printf("%d %d
    ",dist_a,dist_b);
        }
        return 0;
    } 
    
  • 相关阅读:
    【python】绘图,颜色,线型
    【python】绘图,画虚线
    【python】绘图坐标轴标题中包含上标或下标
    【python】python3.7与3.9共存,两个3版本同时存在(平时用vscode敲代码)pip复制
    【python】 matplotlib 画图刻度、图例等字体、字体大小、刻度密度、线条样式设置
    Charles乱码问题
    charles的设置断点
    Charles的设置手机抓包
    ajax
    java jdbc连接mysql数据库实现增删改查操作
  • 原文地址:https://www.cnblogs.com/fengxunling/p/9755195.html
Copyright © 2011-2022 走看看