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\n",Ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LLTYYC/p/11456786.html