zoukankan      html  css  js  c++  java
  • DP4 1014

    烟火

    城镇的主干道上有n个区域,从左到右编号为1到n,每个区域之间相距1个单位距离。在节日中要放m个烟火,第i个烟火会在ti时刻的ai区域放。如果在ti时刻你所处区域为x,那么你可以获得bi - | ai - x |的快乐值。在每个单位时间你可以移动不超过d个单位距离,初始的位置是任意的,求通过移动能获得快乐值和的最大值。

    1 <= n <= 150000,1 <= m <= 300,1 <= d, ai <= n,1 <= bi, ti <= 10^9,对于i >= 2有ti >= ti-1。

    题解

    状态设计很简单f[i][j]表示看了i个烟火,在j的位置的最大快乐值。

    f[i][j]=max{f[i-1][k]}+b[i]-|a[i]-j|

    k是一段连续的区间,并且对于同一个i长度固定,所以可以用单调队列维护,注意可以双向移动。

    注意pos开long long

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=150005;
    const ll inf=1000000000000ll;
    int n,m;
    ll d;
    ll f[2][maxn];
    int a[maxn],b[maxn],T[maxn];
    int h,t,q[maxn];
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    ll abs(ll x){return x<0 ? -x : x ;}
    ll min(ll x,ll y){return x<y ? x : y ;}
    
    int main(){
        freopen("fireworks.in","r",stdin);
        freopen("fireworks.out","w",stdout);
        read(n);read(m);read(d);
        for(int i=1;i<=m;i++) read(a[i]),read(b[i]),read(T[i]);
        for(int i=1;i<=n;i++) f[1][i]=b[1]-abs(a[1]-i);
        for(int i=2;i<=m;i++){
            h=1,t=0;
            int now=i&1,pre=now^1;
            for(int j=1;j<=min(n,1+d*(T[i]-T[i-1]));j++){
                while(h<=t&&f[pre][j]>f[pre][q[t]]) t--;
                q[++t]=j;
            }
            for(int j=1;j<=n;j++){
                while(h<=t&&abs(j-q[h])>d*(T[i]-T[i-1])) h++;
                int p=q[h];
                f[now][j]=f[pre][p]+b[i]-abs(a[i]-j);
                ll pos=j+1+(ll)d*(T[i]-T[i-1]);
                if(pos<=n){
                    while(h<=t&&f[pre][pos]>f[pre][q[t]]) t--;
                  q[++t]=pos;
                }
            }
        }
        ll mx=-inf;
        for(int i=1;i<=n;i++) mx=max(mx,f[m&1][i]);
        printf("%lld",mx);
    }
    /*
    10 2 1
    1 1000 2
    9 1000 4
    */
    fireworks

    购物

    商店里有n个物品,第i个物品的价格为ci元。每个物品只能买一次。商店发行了n张优惠券,每个物品各有一张优惠卷。如果使用了第i张优惠券,可以使该物品便宜di元钱,必须买商品才能够使用相对应的优惠券。第1张优惠卷可以无条件使用,但对于第i>=2张优惠卷,如果需要使用第i张优惠券,则必须先使用xi这张优惠券。
    现在有b元钱,问最多能购买多少商品。

    1 <= n <= 5000,1 <= b <= 10^9,1 <= di < ci <= 10^9,对于i >= 2有1 <= xi < i。

    题解

    显然商品构成了树形结构。

    f[i][j][0/1]表示i的子树中选取j个商品,不使用/使用i的优惠券所花的最少费用。

    最会找最多的商品并且费用<=b

    转移比较好想,但是因为这道题卡空间,但是因为b<=1e9,所以大于1e9的都不用考虑。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int maxn=5005;
    const ll oo=1000000001;
    int n,m;
    int c[maxn],d[maxn];
    int f[maxn][maxn][2];
    int size[maxn];
    int cnt,head[maxn];
    struct edge{
        int x,y,next;
    }e[maxn];
    
    void add(int x,int y){
        e[++cnt]=(edge){x,y,head[x]};
        head[x]=cnt;
    }
    
    template<class T>inline void read(T &x){
        x=0;int f=0;char ch=getchar();
        while(!isdigit(ch)) {f|=(ch=='-');ch=getchar();}
        while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        x = f ? -x : x ;
    }
    
    ll min(ll x,ll y){return x<y ? x : y ;}
    
    void dfs(int x){
        size[x]=1;
        f[x][1][0]=c[x];
        f[x][1][1]=c[x]-d[x];
        f[x][0][0]=0;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y;
            dfs(y);
            for(int j=size[x];~j;j--)
             for(int k=1;k<=size[y];k++){
                  f[x][j+k][0]=min(f[x][j+k][0],f[x][j][0]+f[y][k][0]);
                  f[x][j+k][1]=min(f[x][j+k][1],min(f[y][k][0],f[y][k][1])+f[x][j][1]);
             }
            size[x]+=size[y];
        }
    }
    
    int main(){
        freopen("shopping.in","r",stdin);
        freopen("shopping.out","w",stdout);
        read(n);read(m);
        read(c[1]);read(d[1]);
        for(int i=2;i<=n;i++){
            read(c[i]);read(d[i]);
            int x;read(x);
            add(x,i);
        }
        memset(f,0x3f,sizeof(f));
        cnt=0;
        dfs(1);
        for(int i=n;~i;i--) if(min(f[1][i][1],f[1][i][0])<=m) {printf("%d",i);return 0;}
    }
    /*
    8 9
    4 3
    8 3 1
    2 1 1
    4 2 2
    7 2 2
    3 1 2
    7 3 5
    2 1 3
    */
    shopping

    括号匹配

    给出长度为N的括号序列(只包含(,),[,]),问有多少种方法删掉这些括号的一个子集,使得剩下的括号序列是合法的,请注意不能全部删完。

    100%的数据保证:1 <= N <= 300。

    题解

    考虑在删除一对合法的括号之后,剩下的两个部分的方案数相乘为总方案数(乘法原理)。

    对于当前区间,最左边的可以匹配或者不匹配,递归搜索即可。

    记忆化搜索

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int mod=1000000007;
    const int maxn=305;
    int n;
    ll f[maxn][maxn][2];//[l,r]是否出现了合法序列 
    char s[maxn];
    
    bool cx(int l,int r){return (s[l]=='('&&s[r]==')')||(s[l]=='['&&s[r]==']');
    }
    
    ll dfs(int l,int r,bool opt){
        if(f[l][r][opt]!=-1) return f[l][r][opt];
        if(l>=r) return opt;
        ll ret=dfs(l+1,r,opt)%mod;//不进行匹配 
        if(s[l]=='('||s[l]=='['){
            for(int i=l;i<=r;i++)
           if(cx(l,i))
            ret=(ret+dfs(l+1,i-1,1)*dfs(i+1,r,1)%mod)%mod;
        }
        return f[l][r][opt]=ret;
    }
    
    int main(){
        freopen("parenthesis.in","r",stdin);
        freopen("parenthesis.out","w",stdout);
        scanf("%d%s",&n,s+1);
        memset(f,-1,sizeof(f));
        printf("%lld",dfs(1,n,0));
    }
    parenthesis
  • 相关阅读:
    c++Primer再学习(1)
    c++Primer再学习练习Todo
    感悟(一)
    新目标《C++程序设计原理与实践》
    C++Primer再学习(4)
    开篇
    C++Primer再学习(3)
    C++实现的单例模式的解惑
    使用springboot缓存图片
    springboot h2 database
  • 原文地址:https://www.cnblogs.com/sto324/p/11712925.html
Copyright © 2011-2022 走看看