zoukankan      html  css  js  c++  java
  • Codeforces 797 F Mice and Holes

    F. Mice and Holes
    time limit per test            
    1.5 seconds
    memory limit per test      
    256 megabytes
    input
    standard input
    output
    standard output

    One day Masha came home and noticed n mice in the corridor of her flat. Of course, she shouted loudly, so scared mice started to run to the holes in the corridor.

    The corridor can be represeted as a numeric axis with n mice and m holes on it. ith mouse is at the coordinate xi, and jth hole — at coordinate pjjth hole has enough room for cj mice, so not more than cj mice can enter this hole.

    What is the minimum sum of distances that mice have to go through so that they all can hide in the holes? If ith mouse goes to the hole j, then its distance is |xi - pj|.

    Print the minimum sum of distances.

    Input

    The first line contains two integer numbers nm (1 ≤ n, m ≤ 5000) — the number of mice and the number of holes, respectively.

    The second line contains n integers x1, x2, ..., xn ( - 109 ≤ xi ≤ 109), where xi is the coordinate of ith mouse.

    Next m lines contain pairs of integer numbers pj, cj ( - 109 ≤ pj ≤ 109, 1 ≤ cj ≤ 5000), where pj is the coordinate of jth hole, and cj is the maximum number of mice that can hide in the hole j.

    Output

    Print one integer number — the minimum sum of distances. If there is no solution, print -1 instead.

    Examples
    input
    4 5
    6 2 8 9
    3 6
    2 1
    3 6
    4 7
    4 7
    output
    11
    input
    7 2
    10 20 30 40 50 45 35
    -1000000000 10
    1000000000 1
    output
    7000000130

     题意:n个老鼠m个洞在数轴上,给出老鼠坐标,再给出每个洞的坐标和最多容纳的老鼠数量,问所有的老鼠到洞里所需要的最短距离,不能则输出-1

    分析题目可得,最优方案一定是 某一个洞里容纳了坐标临近的老鼠

    即将洞按坐标排序,老鼠按坐标排序,第i个洞容纳的是第k——s只老鼠,第i+1个洞容纳的是第s+1——t只老鼠

    令f[j][i]表示前j个洞,容纳了前i只老鼠的最短距离

    s[j][i]表示第1——i只老鼠全到第j个洞的距离和,即前缀和

    那么f[j][i]可以由f[j-1][i-1],f[j-1][i-2]……f[j-1][i-容量]转移过来

     动态转移方程:f[j][i]=min(f[j-1][k]+s[j][i]-s[j][k])

    事件复杂度:n²

    可以用单调队列优化

    对于每一个j和i,s[j][i]是定值,f[j][i]=min(f[j-1][k]-s[j][k])+s[j][i]

    对于每一个j,维护一个f[j-1][k]-s[j][k]的单调递增队列

    f[j][i]就等于  队首+s[j][i]

    然后滚动数组滚起来~~

    (虽然不用滚动数组也能A)

    不用滚动数组版:

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n,m;
    int mouse[5001],que[5005];
    ll sum,f[5001][5001],s[5001];
    struct HOLE
    {
        int pos,capa;
    }hole[5001];
    bool cmp(HOLE p,HOLE k) { return p.pos<k.pos; }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&mouse[i]);
        for(int i=1;i<=m;i++) scanf("%d%d",&hole[i].pos,&hole[i].capa),sum+=hole[i].capa;
        if(n>sum) { printf("-1"); return 0; }
        sort(mouse+1,mouse+n+1);
        sort(hole+1,hole+m+1,cmp);
        memset(f,127,sizeof(f));
        f[0][0]=0;
        for(int j=1;j<=m;j++)
        {
            int head=0,tail=0;
            que[0]=0;
            for(int i=1;i<=n;i++) s[i]=s[i-1]+abs(mouse[i]-hole[j].pos);
            for(int i=0;i<=n;i++)
            {
                while(head<tail&&que[head]<i-hole[j].capa) head++;
                while(head<tail&&f[j-1][i]-s[i]<=f[j-1][que[tail-1]]-s[que[tail-1]]) tail--;
                que[tail++]=i;
                f[j][i]=f[j-1][que[head]]+s[i]-s[que[head]];
            }
        }
        printf("%lld",f[m][n]);
    }
    滚动数组:
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int n,m;
    int mouse[5001],que[5005];
    ll sum,f[2][5001],s[5001];
    struct HOLE
    {
        int pos,capa;
    }hole[5001];
    bool cmp(HOLE p,HOLE k) { return p.pos<k.pos; }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&mouse[i]);
        for(int i=1;i<=m;i++) scanf("%d%d",&hole[i].pos,&hole[i].capa),sum+=hole[i].capa;
        if(n>sum) { printf("-1"); return 0; }
        sort(mouse+1,mouse+n+1);
        sort(hole+1,hole+m+1,cmp);
        memset(f,127,sizeof(f));
        f[0][0]=0;
        int j,now=1,pre=0;
        for(j=1;j<=m;j++,swap(now,pre))
        {
            int head=0,tail=0;
            que[0]=0;
            for(int i=1;i<=n;i++) s[i]=s[i-1]+abs(mouse[i]-hole[j].pos);
            for(int i=0;i<=n;i++)
            {
                while(head<tail&&que[head]<i-hole[j].capa) head++;
                while(head<tail&&f[pre][i]-s[i]<=f[pre][que[tail-1]]-s[que[tail-1]]) tail--;
                que[tail++]=i;
                f[now][i]=f[pre][que[head]]+s[i]-s[que[head]];
            }
        }
        printf("%lld",f[pre][n]);
    }
    
    

    while(head<tail&&que[head]<i-hole[j].capa) 不能加等号,因为这一次的范围是以i-hole[j].capa开始
    while(head<tail&&f[pre][i]-s[i]<=f[pre][que[tail-1]]-s[que[tail-1]])  要加等号,可以更新队列中的老鼠的标号,使其更大,更有利于后面的转移
     
    刚开始想的是三维dp,f[j][i][k] 其中k表示最后一个洞容纳了几只老鼠,这一维是冗余的,且难以设计方程
    这一维起的作用是转移状态的上界,完全可以在枚举i时只枚举合法的
  • 相关阅读:
    每日英语:Yahoo's Rally: Made in China
    【Java线程】Callable和Future
    Throwable和Exception的区别
    Java异常分类
    牛客网上的剑指offer题目
    合并两个排序的链表
    反转链表
    链表中倒数第k个结点
    调整数组顺序使奇数位于偶数前面
    数值的整数次方
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6836082.html
Copyright © 2011-2022 走看看