wannalfy 挑战赛7 F Masha与老鼠(贪心+dp)

链接:https://www.nowcoder.net/acm/contest/56/F

 

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

有一天Masha回到家,发现有n只老鼠在它公寓的走廊上,她大声呼叫,所以老鼠们都跑进了走廊的洞中。 
这个走廊可以用一个数轴来表示,上面有n只老鼠和m个老鼠洞。第i只老鼠有一个坐标𝑥𝑖,第𝑗个洞有一个坐标𝑝𝑗和容量𝑐𝑗。容量表示最多能容纳的老鼠数量。 
找到让老鼠们全部都进洞的方式,使得所有老鼠运动距离总和最小。老鼠i进入洞j的运动距离为|𝑥𝑖 − 𝑝𝑗
无解输出-1。

输入描述:

第一行包含两个整数n,m,表示老鼠和洞的数量。
第二行包含n个整数𝑥1...𝑛,表示老鼠坐标。
接下来m行每行两个整数𝑝, 𝑐,表示每个洞的坐标和容量。

输出描述:

输出最小运动距离总和或-1。
示例1

输入

4 5 
6 2 8 9 
3 6 
2 1 
3 6 
4 7
4 7

输出

11
示例2

输入

7 2 
10 20 30 40 50 45 35 
-1000000000 10 
1000000000 1

输出

7000000130

备注:

𝑛, 𝑚 ≤ 10^6, 1 ≤ 𝑐 ≤ 𝑛, 1 ≤ |𝑝|, |𝑥| ≤ 10^9

////////////////////////////////////////////////////////(照例贴题解)
//////////////////////////////////////////////////////(pdf复制文字有毛病,直接放截图了)
(照例贴代码)
#include <bits/stdc++.h>
#define mst(a,b)    memset((a),(b), sizeof a)
#define lowbit(a)   ((a)&(-a))
#define IOS         ios::sync_with_stdio(0);cin.tie(0);
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int maxn=1e6+10;
const ll INF = 1LL<<60;
struct node{
    int x,c;
    bool operator < (const node&p)const{return x<p.x;}
};
node a[maxn<<1];int cnt[maxn<<1];
int use[maxn<<1],head;
struct Sta{
    ll arr[maxn<<2],c;int he;
    inline void push(ll v){arr[++he]=v-c;}
    inline void pop(){--he;}
    inline ll top(){return arr[he]+c;}
}le,ri;
void f(int num){
    int pos=0;
    while(num){
        a[pos++]=num%10;
        num/=10;
    }
}
int main(){
#ifdef local
freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
#endif
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i].x);a[i].c=0;
    }
    ll sum=0;int tmp=n;
    while(m--){
        ++n;    scanf("%d%d",&a[n].x,&a[n].c);
        sum+=a[n].c;
    }
    if(sum<tmp){printf("-1");return 0;}
    sort(a+1,a+1+n);
    for(int i=1,head=0;i<=n;++i){
        if(!a[i].c){
            ++cnt[use[head]];
            if(cnt[use[head]]==a[use[head]].c)--head;
        }else use[++head]=i;
    }
    for(int i=n,head=0;i>0;--i){
        if(!a[i].c){
            ++cnt[use[head]];
            if(cnt[use[head]]==a[use[head]].c)--head;
        }else if(cnt[i]!=a[i].c)use[++head]=i;
    }
    ll ans=0;
    for(int i=1;i<=n;++i){
        if(!a[i].c){//老鼠
            ri.push(ans);ri.c-=a[i].x;
            ans=INF;
            if(le.he){
                ans=le.top()+a[i].x;le.pop();
            }
            le.c+=a[i].x;
        }else if(cnt[i]){//
            le.push(ans);le.c-=a[i].x;
            if(ri.he){
                ans=min(ans,ri.top()+a[i].x);ri.pop();
            }
            ri.c+=a[i].x;
            if(cnt[i]>1)--cnt[i],--i;
        }
    }
    printf("%lld",ans);
    return 0;
}

这道题记得在去年省赛训练的时候遇到过类似的,不过当时刚学啥都不会,也没补

做这道题想了好久(补题更是一天拖过一天),只能想到连线之间不会有交叉,没想到可以贪心贪出2*n个位置,更没想到可以dp

这道题的dp是基于这样一个基础:如果同时有老鼠和老鼠洞处于待被右边选中状态,那这肯定不是最优情况(参照 连线肯定不会有交叉)

放弃老鼠洞只在j=0时可以选择

还有ans=INF那一步不能省,如果没有这一步,当老鼠全部处于最左边时,你的输出答案会是0(你忽略了dp[i][0]为INF即不可能的情况)

还有一个发现:在老鼠洞的情况下,我用while(cnt[i]--)的代码超时了,但是换成了--cnt[i],--i;就过了,while真的有那么慢吗(好惨)

posted on 2018-02-06 14:32  scau_bi  阅读(661)  评论(0编辑  收藏  举报

导航