zoukankan      html  css  js  c++  java
  • 【提高组】

    P1005 矩阵取数游戏 (区间DP)

    区间DP。

    可以看出每行互不影响,所以每次区间DP求出本行最大值,ans即加上每一行最大值。

    转移方程式:f[L][R]=max(num[L]*p[k]+dp(L+1,R),dp(L,R-1)+num[R]*p[k])

    #include<bits/stdc++.h>
    #define For(i,l,r) for(int i=l;i<=r;i++)
    #define lll __int128
    #define ll long long
    using namespace std;
    const int M=81;
    int n,m,num[M];
    lll ans,p[M],f[M][M];
    inline ll read(){
        ll f=1,sum=0;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
        return f*sum;
    }
    inline void write(lll x){
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10+'0');
    }
    inline lll dp(int l,int r){
        if(f[l][r]!=-1) return f[l][r];
        if(r-l>=1) f[l][r]=max(num[l]*p[m-r+l]+dp(l+1,r),dp(l,r-1)+num[r]*p[m-r+l]);
        else f[l][r]=num[l]*p[m-r+l];
        return f[l][r];
    } 
    int main(){
        n=read(),m=read();
        p[0]=1;
        For(i,1,m) p[i]=p[i-1]*2;
        For(i,1,n){
            For(j,1,m) num[j]=read();
            memset(f,-1,sizeof(f));
            ans+=dp(1,m);
        } 
        write(ans);
        return 0;
    }
    View Code

     

    P1373 小a和uim之大逃离

    #蒟蒻看再久都不知从何下手,神犇一眼秒说是基础递推#

    一个比较有趣的dp方程,f[i][j][p][k]中(i,j)表示所处位置,p代表魔液差值,k代表最后一步是小a还是小uim走。a[i][j]即所处位置魔液值。ans加上f[i][j][0][1]。

    转移方程式很好想+k是避免减出负数)

    f[i][j][p][0]+=f[i-1][j][(p-a[i][j]+k)%k][1]

    f[i][j][p][0]+=f[i][j-1][(p-a[i][j]+k)%k][1]

    f[i][j][p][1]+=f[i-1][j][(p+a[i][j])%k][0]

    f[i][j][p][1]+=f[i][j-1][(p+a[i][j])%k][0]

    四维数组注意算空间内存。两次30分因为写成+=,别缩写啦!!

    #include<bits/stdc++.h>
    #define ll long long
    #define For(i,l,r) for(int i=l;i<=r;i++)
    using namespace std;
    const int p=1e9+7;
    int dp[805][805][20][2];
    inline ll read(){
        ll f=1,sum=0;
        char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){sum=(sum<<1)+(sum<<3)+(ch^48);ch=getchar();}
        return f*sum;
    }
    int f[805][805][20][2],n,m,k,a[805][805],ans; 
    int main(){
        scanf("%d%d%d",&n,&m,&k);++k;
        For(i,1,n){
            For(j,1,m){
                scanf("%d",&a[i][j]);
                f[i][j][a[i][j]%k][0]=1;
            }
        }
        For(i,1,n){
            For(j,1,m){
                For(h,0,k){
                    f[i][j][h][0]=(f[i][j][h][0]+f[i-1][j][(h-a[i][j]+k)%k][1])%p;
                    f[i][j][h][0]=(f[i][j][h][0]+f[i][j-1][(h-a[i][j]+k)%k][1])%p;
                    f[i][j][h][1]=(f[i][j][h][1]+f[i-1][j][(h+a[i][j])%k][0])%p;
                    f[i][j][h][1]=(f[i][j][h][1]+f[i][j-1][(h+a[i][j])%k][0])%p;
                }
            }
        }
        For(i,1,n){
            For(j,1,m){
                ans=(ans+f[i][j][0][1])%p;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    P2279 [HNOI2003]消防局的设立(树形DP)

    “树形DP!(喜形于色.jpg)”“撸完树形DP再去撸题解中的神仙贪心!”

    首先F[i][state]表示节点i在状态state下的最小消防局数。

    状态设计为5个

    F[i][0]表示可以覆盖到从节点i向上2层的最小消防站个数

    F[i][1]表示可以覆盖到从节点i向上1层的最小消防站个数(即节点i的整颗子树及其父亲)

    F[i][2]表示可以覆盖到从节点i向上0层的最小消防站个数

    F[i][3]表示可以覆盖到从节点i向上-1层的最小消防站个数(即节点i的子树和它们的子孙)

    F[i][4]表示可以覆盖到从节点i向上-2层的最小消防站个数

    为什么没有向上3层?因为不可能(要求距离在2以内)。为什么没有向上-3层?因为如果F[i][3~4]则已经把这种情况覆盖。

    状态转移

    F[i][0]=1+F[s][4](si的儿子

    F[i][1]=min{ F[s][0](si的儿子)+ F[t][3] (ti的儿子ts} F[i][0]中的最小值

    F[i][2]=min { F[s][1] (si的儿子+ F[t][2] (ti的儿子ts} F[i][0~1]中的最小值

    F[i][3]={ F[s][2] si的儿子)} 和[i][0-2]中的最小值

    F[i][4]={ F[s][3](si的儿子} F[i][0-3]中的最小值

    答案

    我们希望寻找当整棵树都被覆盖的时候的消防站最小值,于是需要覆盖到根的这一层,于是结果是F[1][2]。

    代码

    #include<bits/stdc++.h>
    #define ll long long
    #define ri register int
    #define For(i,l,r) for(int i=l;i<=r;i++)
    using namespace std;
    const int M=1005;
    const int inf=0x3f3f3f3f;
    struct node{
        int to,nxt;
    }e[M];
    int x,n,head[M],f[M][5],cnt;
    inline void add(int u,int v){
        e[++cnt].to=v;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    inline void dfs(int u){
        f[u][0]=1;
        f[u][3]=0;
        f[u][4]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            dfs(v);
            f[u][0]+=f[v][4];
            f[u][3]+=f[v][2];
            f[u][4]+=f[v][3];
        }
        if(!head[u]) f[u][1]=f[u][2]=1;
        else{
            f[u][1]=f[u][2]=inf;
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].to;
                int f1=f[v][0];
                int f2=f[v][1];
                for(int j=head[u];j;j=e[j].nxt){
                    if(i==j)continue;
                    int s=e[j].to;
                    f1+=f[s][3];
                    f2+=f[s][2];
                }
                f[u][1]=min(f[u][1],f1);
                f[u][2]=min(f[u][2],f2);
            }
        }
        For(i,1,4) f[u][i]=min(f[u][i],f[u][i-1]);
    }
    int main(){
        scanf("%d",&n);
        For(i,2,n){
            scanf("%d",&x);
            add(x,i);
        }
        dfs(1);
        printf("%d
    ",f[1][2]);
        return 0;
    }
    View Code

     附

    题解     https://www.luogu.org/blog/rickole/solution-p2279

    P1220 关路灯(区间DP)

    吐槽一波数据太水了,我没过样例的DP都能拿80分。

    区间DP显然,F[i][j][0~1]代表区间i~j的灯都关后在i(0)或j(1)位置处时的最小耗费的电功率。

    转移方程为

    dp[i][j][0]== min(dp[i+1][j][0]+cal(i,i+1,i,j+1), dp[i+1[j][1]+cal(i,j,i,j+1));

    dp[i][j][1]= min(dp[i][j-1][0]+cal(i,j,i-1,j), dp[i][j-1][1]+cal(j-1,j,i-1,j));

    cal()函数计算从上一个区间转移过来时没关的路灯消耗的电力,为加速计算,利用前缀和找到功率之和,p[i]为功率的前缀和数组,loc[i]存储路灯位置。cal(i, ,j ,l ,r)=( loc[j] - loc[i] )*( p[n] - p[r-1]+p[l] )。

    为节省计算,将j放到i外面倒着做i,避免重复计算没更新的值。(朴素想法是从中间更新到两边)。

    #include<bits/stdc++.h>
    #define For(i,l,r) for(int i=l;i<=r;i++)
    #define Dfor(i,r,l) for(int i=r;i>=l;i--)
    using namespace std;
    const int M=55;
    int n,c,loc[M],p[M],f[M][M][2];
    inline int cal(int i,int j,int l,int r){return (loc[j]-loc[i])*(p[n]-p[r-1]+p[l]);}
    int main(){
        scanf("%d%d",&n,&c);
        memset(f,0x3f3f3f,sizeof(f));
        For(i,1,n){
            int tmp;
            scanf("%d%d",&loc[i],&tmp);
            p[i]=p[i-1]+tmp;
        }
        f[c][c][1]=f[c][c][0]=0;
        For(j,c,n)
            Dfor(i,j-1,1){
                f[i][j][0]=min(f[i+1][j][0]+cal(i,i+1,i,j+1),f[i+1][j][1]+cal(i,j,i,j+1));
                f[i][j][1]=min(f[i][j-1][0]+cal(i,j,i-1,j),f[i][j-1][1]+cal(j-1,j,i-1,j));
            }
        printf("%d",min(f[1][n][0],f[1][n][1]));
        return 0;
    }
    View Code
  • 相关阅读:
    传奇版本自动穿背包中的装备脚本
    MaxM2引擎各种人物触发脚本
    异或加密解密代码
    中元节
    网络加密
    MCMD Commander 命令解释及说明
    Ubuntu字符界面与图形界面切换
    传奇服务端各文件用途说明
    传奇数据库说明
    传奇MAP地图说明
  • 原文地址:https://www.cnblogs.com/jian-song/p/11624867.html
Copyright © 2011-2022 走看看