pinball

pinball

题目描述
A喜欢玩一个叫pinball的游戏。游戏规则如下:
Pinball的游戏界面由m+2行、n列组成。第一行在顶端。一个球会从第一行出发,开始垂直下落,A会得到一个积分当他击中一个球的时候。
小天才lyk觉得这太困难了,于是在界面中放入了一些漏斗,一共有m个漏斗分别放在第2~m+1行,第i个漏斗的作用是把经过第i+1行且列数在Ai~Bi之间的球将其移到第Ci列。
但是使用每个漏斗都是需要付钱的,第i个漏斗需要支付Di的价钱,A需要保留一些漏斗,使得球无论从第一行的哪一列开始放,都只可能到达第m+2行的唯一一列。同时,A希望花费最小的价钱。

输入
第一行两个数,m和n
接下来m行,第i+1行描述第i个漏斗的属性,Ai,Bi,Ci,Di(1<=Ai<=Ci<=Bi<=n,1<=Di<=1000000000)。

输出
若不存在一种方案能满足条件则输出-1,否则输出最小话费。

样例输入
5 6
2 4 3 5
1 2 2 8
3 6 5 2
4 6 4 7
2 4 3 10

3 5
2 4 3 10
1 3 1 20
2 5 4 30
样例输出
25

-1
提示
【样例解释1】

好图

如图,只需使用第2、4、5个漏斗即可。

【数据范围】

对于20%的数据,m<=10,n<=1000

对于 40%的数据,m<=200

对于60%的数据,m<=1000

对于100%的数据,m<=100000,2<=n<=1000000000

1n

暴力

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long INF=(long long)1<<60;
struct node{
  int x,y,z;
  long long d;
}a[200050];
int p[600000];
long long dp1[600000],dp2[600000];
int main(){
  int m,n;
  scanf("%d%d",&m,&n);
  if (m>1000){
    cout<<"heSaBi"<<endl;
    return 0;
  }
  int tot=0;
  for (int i=1;i<=m;++i){
    scanf("%d%d%d%lld",&a[i].x,&a[i].y,&a[i].z,&a[i].d);
    p[tot++]=a[i].x;
    p[tot++]=a[i].y;
    p[tot++]=a[i].z;
  }
  p[tot++]=1;
  p[tot++]=n;
  sort(p,p+tot);
  int t=unique(p,p+tot)-p;
  for (int i=1;i<=m;++i){
    a[i].x=lower_bound(p,p+t,a[i].x)-p;
    a[i].y=lower_bound(p,p+t,a[i].y)-p;
    a[i].z=lower_bound(p,p+t,a[i].z)-p;
  }
  for (int i=0;i<t;++i) dp1[i]=INF;
  dp1[lower_bound(p,p+t,1)-p]=0;

  for (int i=0;i<t;++i) dp2[i]=INF;
  dp2[lower_bound(p,p+t,n)-p]=0;

  long long ans=INF;
  for (int i=1;i<=m;++i){
    long long x1=INF;
    for (int j=a[i].x;j<=a[i].y;++j)
      x1=min(x1,dp1[j]);

    long long x2=INF;
    for (int j=a[i].x;j<=a[i].y;++j)
      x2=min(x2,dp2[j]);

    ans=min(ans,x1+x2+a[i].d);

    dp1[a[i].z]=min(dp1[a[i].z],x1+a[i].d);
    dp2[a[i].z]=min(dp2[a[i].z],x2+a[i].d);
  }
  if (ans>=INF) ans=-1;
  printf("%lld\n",ans);
  return 0;
}

线段树优化

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long INF=(long long)1<<60;
struct node{
  int x,y,z;
  long long d;
}a[200050];
int p[600000];
long long dp1[1300000],dp2[1300000];

long long find1(int l,int r,int z,int x,int y){
  if (r<x||l>y) return INF;
  if (x<=l&&r<=y) return dp1[z];
  int mid=(l+r)/2;
  return min(find1(l,mid,z+z,x,y),find1(mid+1,r,z+z+1,x,y));
}

long long find2(int l,int r,int z,int x,int y){
  if (r<x||l>y) return INF;
  if (x<=l&&r<=y) return dp2[z];
  int mid=(l+r)/2;
  return min(find2(l,mid,z+z,x,y),find2(mid+1,r,z+z+1,x,y));
}

void change1(int l,int r,int z,int x,long long h){
  if (l>r) return;
  if (l==r){
    dp1[z]=min(dp1[z],h);
    return;
  }
  int mid=(l+r)/2;
  if (mid>=x)
    change1(l,mid,z+z,x,h);
  else
    change1(mid+1,r,z+z+1,x,h);

  dp1[z]=min(dp1[z+z],dp1[z+z+1]);
}

void change2(int l,int r,int z,int x,long long h){
  if (l>r) return;
  if (l==r){
    dp2[z]=min(dp2[z],h);
    return;
  }
  int mid=(l+r)/2;
  if (mid>=x)
    change2(l,mid,z+z,x,h);
  else
    change2(mid+1,r,z+z+1,x,h);

  dp2[z]=min(dp2[z+z],dp2[z+z+1]);
}

int main(){
  int m,n;
  scanf("%d%d",&m,&n);
  int tot=0;
  for (int i=1;i<=m;++i){
    scanf("%d%d%d%lld",&a[i].x,&a[i].y,&a[i].z,&a[i].d);
    p[tot++]=a[i].x;
    p[tot++]=a[i].y;
    p[tot++]=a[i].z;
  }
  p[tot++]=1;
  p[tot++]=n;
  sort(p,p+tot);
  int t=unique(p,p+tot)-p;
  for (int i=1;i<=m;++i){
    a[i].x=lower_bound(p,p+t,a[i].x)-p;
    a[i].y=lower_bound(p,p+t,a[i].y)-p;
    a[i].z=lower_bound(p,p+t,a[i].z)-p;
  }
  for (int i=1;i<=1200000;++i){
    dp1[i]=INF;
    dp2[i]=INF;
  }

  int hh=lower_bound(p,p+t,1)-p;
  change1(0,t-1,1,hh,0);

  hh=lower_bound(p,p+t,n)-p;
  change2(0,t-1,1,hh,0);

  long long ans=INF;
  for (int i=1;i<=m;++i){

    long long x1=find1(0,t-1,1,a[i].x,a[i].y);

    long long x2=find2(0,t-1,1,a[i].x,a[i].y);

    ans=min(ans,x1+x2+a[i].d);

    change1(0,t-1,1,a[i].z,x1+a[i].d);

    change2(0,t-1,1,a[i].z,x2+a[i].d);

  }
  if (ans>=INF) ans=-1;
  printf("%lld\n",ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值