【CodeForces】988F·Rain and Umbrellas

题目

传送门

题目大意

数轴上有一些区间 (li,ri) ( l i , r i ) 在下雨,在某些点 pi p i 上有伞,伞的重量为 wi w i ,一个点上可能有多把伞,你想通过一个下雨的区间就必须有伞,你可以带很多伞,你行走一个单位长度需要花费你带的所有伞的重量和的代价,问从 0 0 走到a的最小代价。

分析

dp[i][j] d p [ i ] [ j ] 表示拿着第 j j 把伞,走到点i的最小代价(你随时都只最多拿一把伞,但是可能会换伞), j=0 j = 0 表示不拿伞。数据范围比较小,所以直接用 Rain[i] R a i n [ i ] 表示区间 (i1,i) ( i − 1 , i ) 有没有下雨, Umb[i] U m b [ i ] 表示点 i i 上重量最小的伞的编号(显然你只会拿这个点上最轻的伞),Umb[i]=0表示这个点上没有伞。

分三种情况:

  • 如果 (i1,i) ( i − 1 , i ) 没有下雨( Rain[i]=0 R a i n [ i ] = 0 ),就可以在点 i1 i − 1 把伞扔掉,所以:
    dp[i][0]=min(dp[i][0],dp[i1][j])|Rain[i]=0 d p [ i ] [ 0 ] = m i n ( d p [ i ] [ 0 ] , d p [ i − 1 ] [ j ] ) | R a i n [ i ] = 0
  • 不管 (i1,i) ( i − 1 , i ) 有没有下雨,都可以继续拿伞(前提是有伞即 j>0 j > 0 ),所以:
    dp[i][j]=min(dp[i][j],dp[i1][j]+W[j])|j>0 d p [ i ] [ j ] = m i n ( d p [ i ] [ j ] , d p [ i − 1 ] [ j ] + W [ j ] ) | j > 0
  • 如果点 i1 i − 1 上有伞,你就可以换成这把伞(一定是换成 Umb[i1] U m b [ i − 1 ] ),所以:
    dp[i][Umb[i1]]=min(dp[i][Umb[i1]],dp[i1][j]+W[Umb[i1]])|Umb[i1]>0 d p [ i ] [ U m b [ i − 1 ] ] = m i n ( d p [ i ] [ U m b [ i − 1 ] ] , d p [ i − 1 ] [ j ] + W [ U m b [ i − 1 ] ] ) | U m b [ i − 1 ] > 0

注意讨论的是从 i1 i − 1 走到 i i ,如果是从i走到 i+1 i + 1 ,用刷表法即可。
初始化 dp[0][0]=0 d p [ 0 ] [ 0 ] = 0 dp[i][j]= (i0 or j0) d p [ i ] [ j ] = ∞   ( i ≠ 0   o r   j ≠ 0 )

代码

#include<cstdio>
#include<algorithm>
using namespace std;

#define MAXA 2000
#define MAXM 2000
#define INF 0x3f3f3f3f
int N,M,A;
bool Rain[MAXA+5];
int dp[MAXA+5][MAXM+5];
int Umb[MAXA+4],W[MAXM+5];

int main(){
    scanf("%d%d%d",&A,&N,&M);
    for(int i=1;i<=N;i++){
        int l,r;
        scanf("%d%d",&l,&r);
        for(int j=l+1;j<=r;j++)
            Rain[j]=1;
        //Rain[i]: (i-1,i)
    }
    W[0]=INF;//方便更新Umb
    for(int i=1;i<=M;i++){
        int p;
        scanf("%d%d",&p,&W[i]);
        if(W[i]<W[Umb[p]])
            Umb[p]=i;
    }
    fill(dp[0],dp[0]+M+1,INF);//注意这里
    dp[0][0]=0;
    for(int i=1;i<=A;i++){
        fill(dp[i],dp[i]+M+1,INF);
        for(int j=0;j<=M;j++){
            //扔伞
            if(!Rain[i])
                dp[i][0]=min(dp[i][0],dp[i-1][j]);
            //继续拿伞
            if(j>0)
                dp[i][j]=min(dp[i][j],dp[i-1][j]+W[j]);
            //换伞(一定换成Umb[i-1])
            if(Umb[i-1])
                dp[i][Umb[i-1]]=min(dp[i][Umb[i-1]],dp[i-1][j]+W[Umb[i-1]]);
        }
    }
    int Ans=INF;
    for(int i=0;i<=M;i++)//统计Ans,看走到最后拿哪把伞(或者不拿)
        Ans=min(Ans,dp[A][i]);
    if(Ans==INF)
        puts("-1");
    else
        printf("%d",Ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值