bzoj 3753: Wall

题意:

众所周知,GFW的出现促进了社会的和谐。Wayne于是想研究一下GFW。
Wayne喜欢网格,所以他把一些网站排成了N_ M的网格,每个格子代表一个网站。每个网站有一个评分,当然评分可正可负。现在Wayne想用一堵“墙”围住一些网站,使得评分和最大。所谓“墙”,就是一个由网格的边组成的简单多边形,不能自交,也不能有空洞。
过了一会儿Wayne发现这个模型太一般了。出于一些原因,有些网站必须在“墙”外,我们称之为“坏网站”;而有些网站必须在“墙”内,我们称之为“好网站”。当然了,“坏网站”的评分不一定低,“好网站”的评分不一定高。Wayne又想知道,这种情况下,能够得到的最大评分和。注意,并不总是存在合法的方案,所以当无法实现时,输出“Can not establish GFW.”。

题解:

挺好的插头dp。
首先是状态的设计,规定回路中的格子是选中的,除了最下方的和最右方的。
如图:
这里写图片描述
红色格子是在当前回路下选择的区域。
考虑怎么判断一个格子是否被圈住了,用射线法, 每个格子向左做一条射线,假如经过奇数条边线,那么就在圈内,否则在圈外。
转移的时候判断一下当前格子左边有多少下插头就可以了。
code:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
const int mod=100037,inf=1<<30;
int list[2][mod],state[2][mod],hash[mod],num[2],now,ans=-inf;
int map[15][15],n,m,val[15][15];
int get(int s,int p) {return (s>>((p-1)*2))&3;}
bool C[15][15];
void change(int &s,int p,int c) {s^=get(s,p)<<((p-1)*2);s^=c<<((p-1)*2);}
void update(int &x,int y) {x=x>y?x:y;}
void add(int st,int sum)
{
    int s=st%mod;
    while(hash[s]&&list[now][hash[s]]!=st) (s+=1)%=mod;
    if(!hash[s]) hash[s]=++num[now],list[now][num[now]]=st,state[now][num[now]]=sum;
    else update(state[now][hash[s]],sum);
}
bool check(int st)
{
    int tot=0;
    for(int i=1;i<=m+1;i++) if(get(st,i)) tot++;
    return tot==2;
}
void ins(int x,int y,int st,int sum)
{
    int tot=0;for(int i=1;i<=y;i++) tot+=(get(st,i)!=0);
    if((tot&1)&&map[x][y]!=1) add(st,sum+val[x][y]);
    if(!(tot&1)&&map[x][y]!=2) add(st,sum);
}
void solve()
{
    state[0][1]=list[0][1]=now=0;num[0]=1;ans=-inf;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            now^=1;num[now]=0;memset(hash,0,sizeof(hash));
            for(int k=1;k<=num[!now];k++)
            {
                int st=list[!now][k],p=get(st,j),q=get(st,j+1),sum=state[!now][k];
                if(!p&&!q)
                {
                    ins(i,j,st,sum);
                    if(i+1<=n&&j+1<=m) change(st,j,1),change(st,j+1,2),ins(i,j,st,sum);
                }
                else if(!p&&q)
                {
                    if(j+1<=m) ins(i,j,st,sum);
                    if(i+1<=n) change(st,j,q),change(st,j+1,0),ins(i,j,st,sum);
                }
                else if(p&&!q)
                {
                    if(i+1<=n) ins(i,j,st,sum);
                    if(j+1<=m) change(st,j,0),change(st,j+1,p),ins(i,j,st,sum);
                }
                else if(p==1&&q==2) {if(check(st)&&C[i][j]) update(ans,sum);}
                else if(p==2&&q==1) {change(st,j,0);change(st,j+1,0);ins(i,j,st,sum);}
                else if(p==1&&q==1)
                {

                    int top=1;
                    for(int pos=j+2;pos<=m+1;pos++)
                    {
                        int tmp=get(st,pos);
                        if(tmp==1) top++;if(tmp==2) top--;
                        if(!top) {change(st,j,0);change(st,j+1,0);change(st,pos,1);ins(i,j,st,sum);break;}
                    }
                }
                else if(p==2&&q==2)
                {

                    int top=1;
                    for(int pos=j-1;pos;pos--)
                    {
                        int tmp=get(st,pos);
                        if(tmp==2) top++;if(tmp==1) top--;
                        if(!top) {change(st,j,0);change(st,j+1,0);change(st,pos,2);ins(i,j,st,sum);break;}
                    }
                }
            }
        }
        for(int j=1;j<=num[now];j++) list[now][j]<<=2;
    }
}
int main()
{
    scanf("%d %d",&n,&m);n++;m++;
    for(int i=1;i<n;i++)
        for(int j=1;j<m;j++) scanf("%d",&val[i][j]);
    memset(C,true,sizeof(C));
    solve();
    if(ans==-inf) printf("Can not establish GFW.\n");
    else printf("%d\n",ans);
    for(int i=1;i<n;i++)
        for(int j=1;j<m;j++) scanf("%d",&map[i][j]);
    memset(C,false,sizeof(C));bool flag=false;
    for(int i=n;i>=1;i--)
    {
        if(flag) break;
        for(int j=m;j>=1;j--)
        {
            if(map[i][j]==2) {flag=true;break;}
            C[i][j]=true;
        }
    }
    solve();
    if(ans==-inf) printf("Can not establish GFW.\n");
    else printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值