• P3190 [HNOI2007]神奇游乐园


    传送门

    第一道插头 $dp$

    由于讲不清楚所以假装各位早就会插头 $dp$ 了

    首先要的是一个闭合回路,所以可以用括号表示法表示状态,然后大力分类讨论

    $1.$ 没有右插头和下插头

    那么我们可以啥也不干,或者加一个右插头和下插头

    $2.$ 只有下插头没有右插头

    那么我们可以要把下插头继续延伸,可以向下或者向右

    $3.$ 只有右插头没有下插头:

    同理我们可以往下或者往右延伸

     $4.$ 右插头和下插头都有:这个又要分情况讨论

      $a.$ 插头都是左括号,那么要把两个联通还要把右边原来匹配的右括号改成左括号

      

       $b.$ 插头都是右括号,那么要把两个联通还要把左边原本匹配的左括号改成右括号

      

       $c.$ 右插头是左括号,下插头是右括号,那么直接联通即可,不需要改变其他括号

      

       $d.$ 右插头是右括号,下插头是左括号,如果联通则一定形成一个闭合回路,那么如果其他地方没有插头则直接把当前状态计入答案,否则此状态不合法,不用延伸状态(右图是一种不合法状态)

      

    然后根据考虑到的所有状态慢慢转移即可......

    因为数据比较大所以用滚动数组和哈希表维护状态,状态在加入哈希表时更新

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=3e5;
    const ll INF=1e18;
    int n,m,a[107][107],bit[17],cur,pre;
    ll f[2][N+7],Ans=-INF;
    int fir[N+7],from[N+7],to[2][N+7],cntt[2];//哈希表
    inline void insert(int sta,ll val)//插入状态并更新f
    {
        int p=sta%N;
        for(int i=fir[p];i;i=from[i])
        {
            if(to[cur][i]!=sta) continue;
            f[cur][i]=max(f[cur][i],val); return;
        }
        from[++cntt[cur]]=fir[p]; fir[p]=cntt[cur];
        to[cur][cntt[cur]]=sta; f[cur][cntt[cur]]=val;
    }
    void DP()
    {
        cntt[cur]=1; int now,down,right; ll val;
        for(int i=1;i<=n;i++)//枚举行
        {
            for(int j=1;j<=cntt[cur];j++) to[cur][j]<<=2;//每一行末尾,状态统一左移四进制下的一位
            for(int j=1;j<=m;j++)//枚举列
            {
                pre=cur; cur^=1; cntt[cur]=0; memset(fir,0,sizeof(fir));//清空哈希表
                for(int k=1;k<=cntt[pre];k++)//枚举上一行的状态
                {
                    now=to[pre][k]; val=f[pre][k];
                    right=(now>>bit[j-1])%4; down=(now>>bit[j])%4;//提出插头状态
                    if(!down&&!right)//没有插头
                    {
                        insert(now,val);//继续没有插头
                        if(j!=m) insert(now+(1<<bit[j-1])+((1<<bit[j])<<1),val+a[i][j]);//多一个右插头和下插头
                    }
                    if(down&&!right)//只有下插头
                    {
                        insert(now-down*(1<<bit[j])+down*(1<<bit[j-1]),val+a[i][j]);//向下延伸
                        if(j!=m) insert(now,val+a[i][j]);//向右延伸
                    }
                    if(!down&&right)//只有右插头
                    {
                        insert(now,val+a[i][j]);//向下延伸
                        if(j!=m) insert(now-right*(1<<bit[j-1])+right*(1<<bit[j]),val+a[i][j]);//向右延伸
                    }
                    if(down==1&&right==1)//插头为'(('
                    {
                        int cnt=1;
                        for(int l=j+1;l<=m;l++)//找到右边第一个和当前下插头匹配的')'
                        {
                            if((now>>bit[l])%4==1) cnt++;
                            if((now>>bit[l])%4==2) cnt--;
                            if(!cnt)
                            {
                                insert(now-(1<<bit[l])-(1<<bit[j-1])-(1<<bit[j]),val+a[i][j]);//联通并改')'为'('
                                break;
                            }
                        }
                    }
                    if(down==2&&right==2)//插头为'))'
                    {
                        int cnt=1;
                        for(int l=j-2;l>=0;l--)//找到左边第一个和当前右插头匹配的'('
                        {
                            if((now>>bit[l])%4==1) cnt--;
                            if((now>>bit[l])%4==2) cnt++;
                            if(!cnt)
                            {
                                insert(now+(1<<bit[l])-((1<<bit[j-1])<<1)-((1<<bit[j])<<1),val+a[i][j]);//联通并改'('为')'
                                break;
                            }
                        }
                    }
                    if(down==1&&right==2)//')('
                        insert(now-((1<<bit[j-1])<<1)-(1<<bit[j]),val+a[i][j]);//直接联通即可
                    if(down==2&&right==1)//'()'
                        if(now==((1<<bit[j])<<1)+(1<<bit[j-1]))//当前状态只有这两个插头
                            Ans=max(Ans,val+a[i][j]);//统计答案
                }
            }
        }
    }
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=10;i++) bit[i]=(i<<1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++) a[i][j]=read();
        DP();
        printf("%lld
    ",Ans);
        return 0;
    }
  • 相关阅读:
    Spring05_基于注解的IOC和DI
    Spring02_基于XML的IOC
    Spring01_概述及程序的耦合
    设计模式六、单例模式
    设计模式五,建造者模式
    前后端分离异常统一处理
    vue qs.stringify 和JSON.stringify 区别
    设计模式四、抽象工厂模式
    设计模式三、工厂方法模式
    设计模式二、简单工厂模式——静态工厂模式
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/11456786.html
Copyright © 2020-2023  润新知