插头DP-3

bzoj 2310 ParkII
题意:一个 m * n 的矩阵,每个矩阵内有个权值V(i,j) (可能为负数),要求找一条路径,使得每个点最多经过一次,并且点权值之和最大。注意:路径上有可能只有一个点.

#include<bits/stdc++.h>
using namespace std;
struct node{int st,v,next;}data[2][1<<18];
const int mod=10000;
int h[mod],num[2],pre,cur,mp[110][10],n,m,ans;
inline void insert1(int s,int v){//插入状态与权值
    int ss=s%mod;
    for (int i=h[ss];i;i=data[cur][i].next){
        if (s==data[cur][i].st){
            data[cur][i].v=max(data[cur][i].v,v);return;
        }
    }data[cur][++num[cur]].st=s;data[cur][num[cur]].v=v;data[cur][num[cur]].next=h[ss];h[ss]=num[cur];
}
inline int sets(int S,int x,int y,int left,int up){//更新当前点的插头状态
    return S+left*(1<<(x<<1))+up*(1<<(y<<1));
}
inline int getl(int s,int l){//找2插头对应的1插头
    int cnt=1;
    for (int i=l;~i;--i){
        if ((s>>(i<<1))%4==1) --cnt;
        if ((s>>(i<<1))%4==2) ++cnt;
        if (!cnt) return i;
    }
}
inline int getr(int s,int r){//找1插头对应的2插头
    int cnt=1;
    for (int i=r;i<=m;++i){
        if ((s>>(i<<1))%4==1) ++cnt;
        if ((s>>(i<<1))%4==2) --cnt;
        if (!cnt) return i;
    }
}
int main(){
    int n,m;cin>>n>>m;
    for (int i=1;i<=n;++i)for (int j=1;j<=m;++j) cin>>mp[i][j],ans=max(mp[i][j],ans);//输入地图
    pre=0;cur=1;data[pre][++num[pre]].st=0;data[pre][num[pre]].v=0;//状态初始化
    for (int i=1;i<=n;++i){
        for (int j=1;j<=num[pre];++j)
            data[pre][j].st<<=2;//两行间交换要进行状态偏移
        for (int j=1;j<=m;++j){
            num[cur]=0;memset(h,0,sizeof(h));
            for (int k=1;k<=num[pre];++k){
                int now=data[pre][k].st,v=data[pre][k].v,left=now>>(j-1<<1),up=now>>(j<<1);left%=4;up%=4;
                int s=sets(now,j-1,j,-left,-up),x;
                if (!left&&!up){//左上插头皆空
                    insert1(now,v);
                    if (j!=m) insert1(sets(s,j-1,j,0,3),v+mp[i][j]);
                    if (i!=n) insert1(sets(s,j-1,j,3,0),v+mp[i][j]);
                    if (i!=n&&j!=m) insert1(sets(s,j-1,j,1,2),v+mp[i][j]);continue;
                }
                if (!left||!up){//左上插头有一个空
                    if (j!=m) insert1(sets(s,j-1,j,0,up+left),v+mp[i][j]);
                    if (i!=n) insert1(sets(s,j-1,j,up+left,0),v+mp[i][j]);
                    if (left+up==1) x=getr(s,j),insert1(sets(s,x,x,-2,3),v+mp[i][j]);
                    if (left+up==2) x=getl(s,j-1),insert1(sets(s,x,x,-1,3),v+mp[i][j]);
                    if (left+up==3) if (!s) ans=max(ans,v+mp[i][j]);continue;
                }//左上插头皆非空
                if (left==3&&up==3) {if (!s) ans=max(ans,v+mp[i][j]);continue;}
                if (left==1&&up==3) x=getr(now,j),insert1(sets(s,x,x,-2,3),v+mp[i][j]);
                if (left==3&&up==1) x=getr(now,j+1),insert1(sets(s,x,x,-2,3),v+mp[i][j]);
                if (left==2&&up==3) x=getl(now,j-2),insert1(sets(s,x,x,-1,3),v+mp[i][j]);
                if (left==3&&up==2) x=getl(now,j-1),insert1(sets(s,x,x,-1,3),v+mp[i][j]);
                if (left==1&&up==1) x=getr(now,j+1),insert1(sets(s,x,x,-2,1),v+mp[i][j]);
                if (left==2&&up==2) x=getl(now,j-2),insert1(sets(s,x,x,-1,2),v+mp[i][j]);
                if (left==2&&up==1) insert1(s,v+mp[i][j]);
            }
            pre^=1;cur^=1;//两点交替
        }
    }printf("%d\n",ans);//最大权值
    return 0;
}
/*
Sample Input 
2 3 
1 -2 1 
1 1 1 
Sample Output 
5 
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值