zoukankan      html  css  js  c++  java
  • 机房测试:A(数学)+B(二分+曼哈顿距离)+C(性质+二分)

    T1:

     

    分析:

    写出s变换的式子:(((s+a)*b+a)*b)……

    将式子化简:

     又可以把m写成:

     也就是将m拆成一个b进制数,每次贪心地使 i 大的时候xi尽量大,那么就可以花费最小次数凑出m。

    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int
    #define ll long long
    int main()
    {
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        ll S,T,a,b;
        scanf("%lld%lld%lld%lld",&S,&T,&a,&b);
        ll nowb=1,n=0,ans=(1ll<<62);
        while(1){
            ll t=T-nowb*S;
            if(t<0) break;
            if(t%a==0){
                ll tot=n,m=t/a;
                ll bb=nowb;
                while(m){//把m拆成b进制数 
                    tot+=m/bb;
                    m%=bb;
                    bb/=b;
                }
                ans=min(ans,tot);
            }
            n++; nowb=nowb*b;
        }
        printf("%d
    ",ans==(1ll<<62) ? -1 : ans);
    }
    /*
    10 28 4 2
    4685 56756 32764 43856
    6 207 5 3
    10000 100000000 3 5
    179 222963 59 384
    
    */
    View Code

    T2:

     

     分析:

    很显然可以二分一个答案,考虑怎么check。

    首先将不满足条件的提出来,对于长的边,想办法将其缩短,但有一种错误的想法:对不满足的点对取交,然后将点对设在交集处。

    为什么是错的呢?

    比如下面这一组数据:

    1 2 3 4 5 6 7

    2 7

    3 6

    如果按照刚刚那样做的话,会将点对设在3 6处(交集),输出2

    而正确的设法应该在2 6设,3可以先走到2再走到6,输出1

    下面考虑正解:

    设最终设定的两个点为u,v。

    任意一个点对x,y的贡献是这两种间取min:1. y-x     2. abs(x-u)+abs(y-v)

    然后答案就是所有的取max

    我们已经二分了一个mid,对于前一种小于等于mid的可以直接跳过,但后一种未知u,v,所以应该怎么处理呢?

    abs(x-u)+abs(y-v)<=mid 是曼哈顿距离的形式。

    可以将(x,y)看做平面上的一个点,所有不超过mid的范围即平面上的一个菱形,只需要对每一个点对做出一个菱形后求交即可。

    但菱形不好求交,转换成正方形:坐标这样转换(x,y)->(x-y,x+y)

    为什么呢?相当于将菱形的四条边向左边延长

    像这样:

     可以看到有两个解析式,将其移项即可知道x,y的转换关系了。

    那为什么不考虑b和c分别是什么呢?

    因为只需要一种映射关系,判断他们能不能有交就可以了,具体交是多少不需要知道。

    然后用lx和rx分别记录一下交的范围,判断是否不符合的都有交即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define N 100005
    #define ri register int
    #define inf (1<<30)
    int L[N],R[N],n,m;
    struct node { int l,r; };
    void work(node &x)
    {
        int ll=x.l,rr=x.r;
        x.l=ll-rr; x.r=ll+rr;//将菱形转换成正方形 
    }
    bool check(int mid)
    {
        int lx=-inf,rx=inf,ly=-inf,ry=inf;
        for(ri i=1;i<=m;++i){
            if(R[i]-L[i]<=mid) continue;
            node x=(node){L[i]+mid,R[i]};
            node y=(node){L[i]-mid,R[i]};
            work(x); work(y);
            lx=max(lx,y.l),rx=min(rx,x.l);//求交 lx rx分别表示x轴上的有交的范围 ly ry同理 
            ly=max(ly,y.r),ry=min(ry,x.r);
            if(lx>rx || ly>ry) return false;
        }
        return true;
    }
    int main()
    {
        freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
        scanf("%d%d",&n,&m);
        int mx=0;
        for(ri i=1;i<=m;++i) scanf("%d%d",&L[i],&R[i]),mx=max(R[i]-L[i],mx);
        int l=0,r=mx+1,ans=inf;
        while(l<r){
            int mid=(l+r)>>1;
            if(check(mid)) r=mid,ans=min(ans,mid);
            else l=mid+1;
        }
        printf("%d
    ",ans);
    }
    /*
    6 2
    1 5
    2 6
    
    9 4
    1 4
    1 8
    5 6
    4 8
    
    5 17
    1 4
    1 1
    1 1
    1 3
    3 4
    3 4
    1 4
    2 2
    1 4
    2 3
    2 4
    1 3
    3 4
    1 3
    1 2
    1 4
    1 4
    */
    View Code

    T3:

     

     分析:

    有一个很重要的性质:

     有了这个性质之后直接二分一个k,先让小B交换完k轮,小A再去交换,看总次数是否小于等于k即可。

    任意交换数组中两个元素,使得有序的最小交换次数 求法:

    4 3 1 2 5

    先让4和本该属于1位置的1交换,用mp记录1的位置,直接交换即可。

    注意交换后mp[4]的值会被改变!!

    #include<bits/stdc++.h>
    using namespace std;
    #define ri register int
    #define N 400005
    int a[N],s[N],x[N],y[N],n,mp[N],vis[N];
    bool check(int k)
    {
        memcpy(a,s,sizeof(s));
        memset(vis,0,sizeof(vis));
        for(ri i=1;i<=k;++i) swap(a[x[i]],a[y[i]]);
        int cnt=0,i=0;
        for(ri i=0;i<n;++i) mp[a[i]]=i;
        for(ri i=0;i<n;++i)
        if(a[i]!=i){//每次找到一个位置不符合的 把后面应该属于这个位置的交换过来 
            int xx=a[i],yy=a[mp[i]];
            swap(a[i],a[mp[i]]),cnt++;
            mp[xx]=mp[i];//!!!注意mp也换了位置!! 
        }
        return cnt<=k;
    }
    int main()
    {
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
        scanf("%d",&n);
        for(ri i=0;i<n;++i) scanf("%d",&s[i]);
        for(ri i=1;i<=n*2;++i) scanf("%d%d",&x[i],&y[i]);
        int l=0,r=2*n+1,ans;
        while(l<r){
            int mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid;
            else l=mid+1;
        }
        printf("%d
    ",ans);
    }
    /*
    3
    1 0 2
    1 2
    0 2
    0 0
    0 1
    0 2
    1 2
    */
    View Code
  • 相关阅读:
    POJ 1328 Radar Installation
    POJ 1700 Crossing River
    POJ 1700 Crossing River
    poj 3253 Fence Repair (贪心,优先队列)
    poj 3253 Fence Repair (贪心,优先队列)
    poj 3069 Saruman's Army(贪心)
    poj 3069 Saruman's Army(贪心)
    Redis 笔记与总结2 String 类型和 Hash 类型
    数据分析方法有哪些_数据分析方法
    数据分析方法有哪些_数据分析方法
  • 原文地址:https://www.cnblogs.com/mowanying/p/11800837.html
Copyright © 2011-2022 走看看