[USACO21DEC] Paired Up G

题目描述

数轴上总计有 N N N 1 ≤ N ≤ 1 0 5 1\le N\le 10^5 1N105)头奶牛。第 i i i 头奶牛的位置为 x i x_i xi 0 ≤ x i ≤ 1 0 9 0 \leq x_i \leq 10^9 0xi109),而第 i i i 头奶牛的重量为 y i y_i yi 1 ≤ y i ≤ 1 0 4 1 \leq y_i \leq 10^4 1yi104)。

根据 Farmer John 的信号,某些奶牛会组成对,使得

  • 每一对包含位置相差不超过 K K K 的两头不同的奶牛 a a a b b b 1 ≤ K ≤ 1 0 9 1\le K\le 10^9 1K109);也就是说, ∣ x a − x b ∣ ≤ K |x_a-x_b|\le K xaxbK

  • 每一头奶牛要么包含在恰好一对中,要么不属于任何一对。

  • 配对是极大的;也就是说,没有两头未配对的奶牛可以组成对。

你需要求出未配对的奶牛的重量之和的可能的范围。具体地说,

  • 如果 T = 1 T=1 T=1,计算未配对的奶牛的最小重量和。

  • 如果 T = 2 T=2 T=2,计算未配对的奶牛的最大重量和。

题解

T = 1 T=1 T=1 T = 2 T=2 T=2 本质上是一样的,把 y i y_i yi 全部取其相反数即可相互转换。下面只考虑 T = 1 T=1 T=1 的情况。

由于配对是极大的,所以每个未配对的奶牛之间距离大于 k k k,并且剩下的奶牛要能全部匹配。

显然让相邻的两头奶牛匹配是最优的。

考虑 d p dp dp 求解。

f i , j f_{i,j} fi,j 表示处理了前 i i i 个奶牛,未配对的奶牛数是奇数还是偶数( j ∈ { 0 , 1 } j\in\{0,1\} j{0,1})。

对于 f i , j f_{i,j} fi,j,若 j = i % 2 j=i\%2 j=i%2,表示配对的奶牛数是偶数,否则为奇数。

初始状态 f 0 , 0 = 0 f_{0,0}=0 f0,0=0

n o w now now 为第 i i i 头奶牛前面到 i i i 距离大于 k k k 的最大的奶牛位置。

j = i % 2 j=i\%2 j=i%2

下面分类讨论

  • i i i 前面选择了偶数头奶牛,并且不选 i i i。显然 i i i 前面选的奶牛都是已配对的。因为 i i i 未配对,所以位置大于 n o w now now 小于 i i i的奶牛都必须配对。转移为 f i , j = min ⁡ ( f i , j , f n o w , 1 − j + y i ) f_{i,j}=\min(f_{i,j},f_{now,1-j}+y_i) fi,j=min(fi,j,fnow,1j+yi)
  • i i i 前面选择了偶数头奶牛,并且选 i i i。这时第 i i i 头奶牛需要与后面的奶牛配对,因此转移条件为 x i + 1 − x i ≤ k x_{i+1}-x_i\le k xi+1xik。前面 i i i 头奶牛匹配了奇数头奶牛,转移方程为 f i , 1 − j = min ⁡ ( f i , 1 − j , f i − 1 , 1 − j ) f_{i,1-j}=\min(f_{i,1-j},f_{i-1,1-j}) fi,1j=min(fi,1j,fi1,1j)
  • i i i 前面选择了奇数头奶牛,并且选 i i i。这时第 i − 1 i-1 i1 头奶牛是未匹配的,它需要与第 i i i 头奶牛匹配。转移条件为 x i − x i − 1 ≤ k x_i-x_{i-1}\le k xixi1k。转移方程为 f i , 1 − j = min ⁡ ( f i , 1 − j , f i − 1 , j ) f_{i,1-j}=\min(f_{i,1-j},f_{i-1,j}) fi,1j=min(fi,1j,fi1,j)
  • i i i 前面选择了奇数头奶牛,并且不选 i i i。这时第 i − 1 i-1 i1 头奶牛是未匹配的,它需要与第 i + 1 i+1 i+1 头奶牛匹配。转移条件为 x i + 1 − x i − 1 ≤ k x_{i+1}-x_{i-1}\le k xi+1xi1k。转移方程为 f i , 1 − j = min ⁡ ( f i , 1 − j , f n o w , j + y i ) f_{i,1-j}=\min(f_{i,1-j},f_{now,j}+y_i) fi,1j=min(fi,1j,fnow,j+yi)

里面的状态要仔细品味。

代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1,INF=1e9+1;
int t,n,k,x[N],y[N],fl=1,now;
int f[N][2];
int main()
{
    scanf("%d%d%d",&t,&n,&k);
    if(t==2) fl=-1;
    for(int i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]),y[i]*=fl;
    x[0]=-INF,x[n+1]=INF;
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=n;i++){
        while(x[i]-x[now+1]>k) now++;
        int j=i&1;
        f[i][j]=min(f[i][j],f[now][j^1]+y[i]);
        if(x[i+1]-x[i]<=k) f[i][j^1]=min(f[i][j^1],f[i-1][j^1]);
        if(x[i]-x[i-1]<=k) f[i][j]=min(f[i][j],f[i-1][j]);
        if(x[i+1]-x[i-1]<=k) f[i][j^1]=min(f[i][j^1],f[now][j]+y[i]);
    }
    printf("%d",f[n][n&1]*fl);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值