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
64bit IO Format: %lld
题目描述
有一天Masha回到家,发现有n只老鼠在它公寓的走廊上,她大声呼叫,所以老鼠们都跑进了走廊的洞中。
这个走廊可以用一个数轴来表示,上面有n只老鼠和m个老鼠洞。第i只老鼠有一个坐标𝑥𝑖,第𝑗个洞有一个坐标𝑝𝑗和容量𝑐𝑗。容量表示最多能容纳的老鼠数量。
找到让老鼠们全部都进洞的方式,使得所有老鼠运动距离总和最小。老鼠i进入洞j的运动距离为|𝑥𝑖 − 𝑝𝑗|
无解输出-1。
这个走廊可以用一个数轴来表示,上面有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真的有那么慢吗(好惨)