F. Mice and Holes
先给老鼠和洞排序,然后dp解之dp[i][j]表示前i个洞进了j个老鼠的最小cost,很容易想到
O(n∗n∗m)
的做法。然后试了下类似多重背包的方法去优化,结果T了。。
看了下官方题解,给的方案是用单调队列优化。因为我们要从dp[i-1][j-1],dp[i-1][j-2],dp[i-1][j-3]…转移到dp[i][j],每个不同的状态转移过去需要增加的cost不一样。但是如果我们给dp[i-1][j-1],dp[i-1][j-2],dp[i-1][j-3]减去一个差量,使得转移所需增量一样,又由于dp[i][]具有单调性,可以用单调队列维护,那么每个状态就可以在
O(1)
时间内计算出来。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll INF = 0x3f3f3f3f3f3f3f;
const int Max = 5005;
ll pm[Max];
pair<ll,int> h[Max];
ll sum[Max];
ll dp[2][Max];
struct OrderedQueue{
ll val[Max];
int id[Max];
int l,r;
void init(){
l = 0;
r = 0;
}
void push(ll v,int i){
while(l<r && v<val[r-1]){
r--;
}
val[r] = v;
id[r] = i;
r++;
}
ll front(){
return val[l];
}
void remove(int i){
while(l<r && id[l]<=i){
l++;
}
}
}que;
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>pm[i];
}
int tot = 0;
for(int i=1;i<=m;i++){
cin>>h[i].first>>h[i].second;
tot += h[i].second;
}
if(tot < n){
cout<<-1<<endl;
return 0;
}
sort(pm+1,pm+n+1);
sort(h+1,h+m+1);
for(int i=0;i<Max;i++){
dp[0][i] = dp[1][i] = INF;
}
int cur = 0;
int pre = 1;
dp[cur][0] = 0;
dp[pre][0] = 0;
for(int i=1;i<=m;i++){
swap(cur,pre);
for(int j=1;j<=n;j++){
sum[j] = sum[j-1] + abs(h[i].first - pm[j]);
}
que.init();
for(int j=0;j<=n;j++){
que.push( dp[pre][j] - sum[j], j);
dp[cur][j] = que.front() + sum[j];
que.remove( j - h[i].second);
}
}
cout<<dp[cur][n]<<endl;
return 0;
}