zoj 3213(插头DP 一条路径)

16 篇文章 0 订阅

 

Beautiful Meadow
Time Limit: 5 Seconds       Memory Limit: 32768 KB

Tom's Meadow

Tom has a meadow in his garden. He divides it into N * M squares. Initially all the squares are covered with grass and there may be some squares cannot be mowed.(Let's call them forbidden squares.) He wants to mow down the grass on some of the squares. He must obey all these rules:

1 He can start up at any square that can be mowed. 
2 He can end up at any square that can be mowed.
3 After mowing one square he can get into one of the adjacent squares. 
4 He cannot get into any of the forbidden squares.
5 He cannot get into the squares that he has already mowed.
6 If he is in some square he must mow it first. (and then decide whether to mow the adjacent squares or not.)
7 Each square that can be mowed has a property D called beauty degree (D is a positive integer) and if he mowed the square the beauty degree of the meadow would increase by D.
8 Note that the beauty degree of the meadow is 0 at first.
9 Of course he cannot move out of the meadow. (Before he decided to end.)
Two squares are adjacent if they share an edge.

Here comes the problem. What is the maximum beauty degree of the meadow Tom can get without breaking the rules above.

Input

This problem has several test cases. The first line of the input is a single integer T (1 <= T < 60) which is the number of test cases. T consecutive test cases follow. The first line of each test case is a single line containing 2 integers N (1 <= N < 8) and M (1 <= M < 8) which is the number of rows of the meadow and the number of columns of the meadow. Then N lines of input describing the rows of the meadow. Each of the N lines contains M space-separated integers D (0 <= D <= 60000) indicating the beauty degree of the correspoding square. For simplicity the beauty degree of forbidden squares is 0. (And of course Tom cannot get into them or mow them.)

Output

For each test case output an integer in a single line which is maximum beauty degree of the meadow at last.

Sample Input

2
1 1
10
1 2
5 0

Sample Output

10
5

 

Author: CAO, Peng
Source: ZOJ Monthly, June 2009

‍‍分析:这一为插头DP简单路径,一直写一条回路的插头DP,做这题时,本以为只要在原来的基础上加上空插头和一些讨论,事实如此,不过讨论情况之多,实在是难以接受,wa了一次,参考傻崽的代码发现少了几种讨论(我原来以为不是必须的),哎。。。还是那个模板,这回比较慢了,看来有必要优化一下work()函数,哎~~~

代码:

#include<cstdio>
#include<cstring>
using namespace std;
const int mm=10007;
struct data
{
    int s[mm],h[mm],p[mm],t,d[mm];
    int hash(int x,int dd)
    {
        int i,c=x%mm;
        for(i=h[c];i>=0;i=p[i])
        if(s[i]==x)
        {
            if(dd>d[i])d[i]=dd;
            return i;
        }
        s[t]=x,p[t]=h[c],h[c]=t,d[t]=dd;
        return t++;
    }
    void clear()
    {
        t=0,memset(h,-1,sizeof(h));
    }
}f[2];
int i,j,g1,g2,u,k,n,m,a,b,a1,a2,b1,b2,g[13][13],ans;
int eat(int s,int a,int b,int c,bool f,bool l)
{
    int n=1,x;
    while(n)
    {
        if(f)a<<=2,b<<=2,c<<=2;
        else a>>=2,b>>=2,c>>=2;
        x=s&c;
        if(x==a)++n;
        if(x==b)--n;
    }
    if(l)return (s^b)|a;else return s|c;
}
void work(int S,int d)
{
    int x,y,dd=d+g[i][j];
    x=a&S,y=b&S;
    if(x==0&&y==0)//两个空插头
    {
        if(g[i+1][j]&&g[i][j+1])f[g2].hash(S|a1|b2,dd);
        if(g[i+1][j])f[g2].hash(S|a,dd);
        if(g[i][j+1])f[g2].hash(S|b,dd);
        f[g2].hash(S,d);
    }
    else if(x==0&&y==b)//右插头为空,下插头为独立插头
    {
        if(g[i][j+1])f[g2].hash(S,dd);
        if(g[i+1][j])f[g2].hash((S^b)|a,dd);
        if((S^b)==0&&dd>ans)ans=dd;
    }
    else if(x==a&&y==0)//下插头为空,右插头为独立插头

    {
        if(g[i+1][j])f[g2].hash(S,dd);
        if(g[i][j+1])f[g2].hash((S^a)|b,dd);
        if((S^a)==0&&dd>ans)ans=dd;
    }
    else if(x==0&&y==b1)//右插头为空,下插头为左括号插头
    {
        if(g[i][j+1])f[g2].hash(S,dd);
        if(g[i+1][j])f[g2].hash((S^b1)|a1,dd);
        f[g2].hash(eat(S^b1,b1,b2,b,1,0),dd);
    }
    else if(x==a1&&y==0)//下插头为空,右插头为左括号插头
    {
        if(g[i+1][j])f[g2].hash(S,dd);
        if(g[i][j+1])f[g2].hash((S^a1)|b1,dd);
        f[g2].hash(eat(S^a1,a1,a2,a,1,0),dd);
    }
    else if(x==0&&y==b2)//右插头为空,下插头为右括号插头
    {
        if(g[i][j+1])f[g2].hash(S,dd);
        if(g[i+1][j])f[g2].hash((S^b2)|a2,dd);
        f[g2].hash(eat(S^b2,b2,b1,b,0,0),dd);
    }
    else if(x==a2&&y==0)//下插头为空,右插头为右括号插头
    {
        if(g[i+1][j])f[g2].hash(S,dd);
        if(g[i][j+1])f[g2].hash((S^a2)|b2,dd);
        f[g2].hash(eat(S^a2,a2,a1,a,0,0),dd);
    }
    else if(x==a1&&y==b)f[g2].hash(eat(S^a1^b,a1,a2,a,1,0),dd);//下插头为独立插头,右插头为左括号插头
    else if(x==a2&&y==b)f[g2].hash(eat(S^a2^b,a2,a1,a,0,0),dd);//下插头为独立插头,右插头为右括号插头
    else if(x==a&&y==b1)f[g2].hash(eat(S^a^b1,b1,b2,b,1,0),dd);//右插头为独立插头,下插头为左括号插头
    else if(x==a&&y==b2)f[g2].hash(eat(S^a^b2,b2,b1,b,0,0),dd);//右插头为独立插头,下插头为右括号插头
    else if(x==a1&&y==b1)f[g2].hash(eat(S^a1^b1,b1,b2,b,1,1),dd);//下插头 右插头为左括号插头
    else if(x==a2&&y==b2)f[g2].hash(eat(S^a2^b2,a2,a1,a,0,1),dd);//下插头 右插头为右括号插头
    else if(x==a2&&y==b1)f[g2].hash(S^a2^b1,dd);//右插头为右括号插头,下插头为左括号插头
    else if(x==a&&y==b&&(S^a^b)==0&&dd>ans)ans=dd;//下插头 右插头为独立插头
}
int DP()
{
    f[0].clear();
    f[0].hash(0,0);
    for(--n,--m,g1=1,g2=i=0;i<=n;++i)
    {
        for(k=0;k<f[g2].t;++k)f[g2].s[k]<<=2;
        a=3,b=3<<2,a1=1,a2=2,b1=1<<2,b2=2<<2;
        for(j=0;j<=m;a<<=2,b<<=2,a1<<=2,a2<<=2,b1<<=2,b2<<=2,++j)
            if(g[i][j])for(g1=!g1,g2=!g2,f[g2].clear(),k=0;k<f[g1].t;++k)work(f[g1].s[k],f[g1].d[k]);
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(g,0,sizeof(g));
        for(ans=i=0;i<n;++i)
            for(j=0;j<m;++j)
            {
                scanf("%d",&g[i][j]);
                if(g[i][j]>ans)ans=g[i][j];
            }
        printf("%d/n",DP());
    }
    return 0;
}


 更新一下代码:

#include<cstdio>
#include<cstring>
#define mm 100007
#define mn 11
#define getRP(a,b) ((a)<<((b)<<1))
struct hash
{
    int h[mm],s[mm],p[mm],d[mm],t;
    int push(int x,int v)
    {
        int i,c=x%mm;
        for(i=h[c];i>=0;i=p[i])
        if(s[i]==x)
        {
            if(v>d[i])d[i]=v;
            return i;
        }
        d[t]=v,s[t]=x,p[t]=h[c],h[c]=t;
        return t++;
    }
    void clear()
    {
        t=0;
        memset(h,-1,sizeof(h));
    }
}f[2];
int g[mn][mn];
int i,j,k,g1,g2,x,y,z,s,n,m,ans;
int eat(bool f,bool l)
{
    int a=getRP(z,j+f),b=getRP(3^z,j+f),c=getRP(3,j+f),n=1;
    s=s^getRP(x,j)^getRP(y,j+1);
    while(n)
    {
        if(f)a<<=2,b<<=2,c<<=2;
        else a>>=2,b>>=2,c>>=2;
        x=s&c;
        if(x==a)++n;
        if(x==b)--n;
    }
    return l?(s|c):((s^b)|a);
}
bool ok(int c)
{
    if(c==1)return g[i+1][j];
    if(c==2)return g[i][j+1];
    if(c==3)return g[i+1][j]&&g[i][j+1];
    return 0;
}
void move(int v)
{
    int w=v+g[i][j];
    if(!x&&!y)
    {
        if(ok(1))f[g2].push(s|getRP(3,j),w);
        if(ok(2))f[g2].push(s|getRP(3,j+1),w);
        if(ok(3))f[g2].push(s|getRP(9,j),w);
        f[g2].push(s,v);
    }
    else if(!x||!y)
    {
        z=x+y;
        if(ok(x?1:2))f[g2].push(s,w);
        if(ok(x?2:1))f[g2].push(s^getRP(z,j)^getRP(z,j+1),w);
        if(z<3)f[g2].push(eat(z==1,1),w);
        else if((s^getRP(x,j)^getRP(y,j+1))==0&&w>ans)ans=w;
    }
    else if(x==y)
    {
        if((z=x)<3)f[g2].push(eat(z==1,0),w);
        else if((s^getRP(x,j)^getRP(y,j+1))==0&&w>ans)ans=w;
    }
    else if(x>2||y>2)
    {
        z=x<y?x:y;
        f[g2].push(eat(z==1,1),w);
    }
    else if(x==2&&y==1)f[g2].push(s^getRP(x,j)^getRP(y,j+1),w);
}
int PlugDP()
{
    f[0].clear();
    f[0].push(0,0);
    for(g1=1,g2=i=0;i<n;++i)
    {
        for(j=0;j<f[g2].t;++j)f[g2].s[j]<<=2;
        for(j=0;j<m;++j)
            if(g[i][j])for(g1=!g1,g2=!g2,f[g2].clear(),k=0;k<f[g1].t;++k)
            {
                s=f[g1].s[k],x=(s>>(j<<1))&3,y=(s>>((j+1)<<1))&3;
                move(f[g1].d[k]);
            }
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        memset(g,0,sizeof(g));
        for(ans=i=0;i<n;++i)
            for(j=0;j<m;++j)
            {
                scanf("%d",&g[i][j]);
                if(g[i][j]>ans)ans=g[i][j];
            }
        printf("%d\n",PlugDP());
    }
    return 0;
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值