HDU 3377 Plan

Problem Description

One day, Resty comes to an incredible world to seek Eve -- The origin of life. Lilith, the sister of Eve, comes with him. Although Resty wants to find Eve as soon as possible, Lilith likes to play games so much that you can‘t make her make any move if you don‘t play with her.

Now they
comes to the magical world and Lilish ask Resty to play with her.

The
game is following :
Now the world is divided into a m * n grids by Lilith,
and Lilith gives each grid a score.
So we can use a matrix to describe
it.
You should come from cell(0, 0) to cell(m-1, n-1) (Up-Left to Down-Right)
and try to colloct as more score as possible.
According to Lilish‘s rule, you
can‘t arrive at each cell more than once.

Resty knows that Lilish will be
easy to find the max score, and he doesn‘t want to lose the game.
So he want
to find the game plan to reach the max score.

Your task is to calculate
the max score that Lilish will find, the map is so small so it shouldn‘t be
difficult for you, right?

Input

The input consists of more than one
testdata.
Process to the END OF DATA.
For each test data :
the first
live give m and n. (1<=m<=8, 1<=n<=9)
following m lines, each
contain n number to give you the m*n matrix.
each number in the matrix is
between -2000 and 2000

Output

Output Format is "Case ID: ANS" one line for each
data
Don‘t print any empty line to the output

Sample Input

2 2

1 2

3 1

3 3

0 -20 100

1 -20 -20

1 1 1

Sample Output

Case 1: 5

Case 2: 61

插头dp

左上角走到右下角,不得重复经过格子,也可以不经过,求最大分数。
两种方法,第一是上下加两行,加两列,加障碍,使题目变成简单的回路,而不是单路。

第二在起点终点都加一个单插头处理,其他地方也应相应微调,不过我好像调得不好,一直WA……233,不过本机测了数百组数据都是对哒,所以不管了……

/*第一种方法*/
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mn=977147;
int i;
struct na{
    int x,z;
    na(int xx,int zz):x(xx),z(zz){}
};
int n,m,x,y,z,a[21],k,en,u=0,p1,p2;
bool map[21][21];
int f[2][mn+1],ans;
int v[2][mn+1];
int re[21][21];
queue <na> q;
inline int gx(int x,int q1,int q2){k=0;for (register int i=m+1;i;i--) k=k*3+(i==x?q1:(i==x+1?q2:a[i]));return k;}
inline void up(int x,int z,int lj,bool la){
    if (la) lj+=re[x/m+1][x%m+1];
    x++;
    k=x%2;
    if (v[k][z]!=x) v[k][z]=x,f[k][z]=-1e9,q.push(na(x,z));
    f[k][z]=max(f[k][z],lj);
}
int main(){
    //freopen("a.in","r",stdin);
    register int i,j;
    while(scanf("%d%d",&n,&m)!=EOF){
        u++;
        printf("Case %d: ",u);
        ans=-1e9;
        memset(map,0,sizeof(map));memset(v,0,sizeof(v));memset(re,0,sizeof(re));memset(f,0,sizeof(f));
        for (i=1;i<=m;i++)
        for (j=1;j<=n;j++)
        map[i][j+2]=1;
        for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
        scanf("%d",&re[i+2][j]);
        n+=4;
        m+=2;
        en=n*m-1;
        for (i=1;i<=m;i++) map[i][1]=map[i][n]=1;
        for (i=1;i<=n;i++) map[m][i]=1;
        map[1][2]=1;map[m-2][n-1]=1;
        if (n==1&&m==1){
            printf("%d\n",re[1][1]);
            continue;
        }
        f[0][0]=0;v[0][0]=1;
        q.push(na(0,0));
        while(!q.empty()){
            na no=q.front();q.pop();
            int an=f[no.x%2][no.z];
            if(no.x%m==0) no.z*=3;
            x=no.x%m+1;y=no.x/m+1;
            for (i=1;i<=m+1;i++) a[i]=0;
            for (i=1,j=no.z;j;i++,j/=3) a[i]=j%3;
            if (!map[x][y])up(no.x,gx(x,0,0),an,0);else
            if (a[x]==1&&a[x+1]==2){
                if (no.x==en) ans=max(ans,an);
            }else if (a[x]==2&&a[x+1]==1) up(no.x,gx(x,0,0),an,1);else
                if (a[x]==0&&a[x+1]==0){
                if (no.x!=en&&no.x!=1) up(no.x,gx(x,0,0),an,0);
                if (map[x][y+1]&&map[x+1][y]) up(no.x,gx(x,1,2),an,1);
            }else if (a[x]==0){
                if (map[x+1][y]) up(no.x,gx(x,0,a[x+1]),an,1);
                if (map[x][y+1]) up(no.x,gx(x,a[x+1],0),an,1);
            }else if (a[x+1]==0){
                if (map[x+1][y]) up(no.x,gx(x,0,a[x]),an,1);
                if (map[x][y+1]) up(no.x,gx(x,a[x],0),an,1);
            }else if (a[x]==a[x+1]){
                p1=p2=0;
                if (a[x]==1)
                for (j=0,i=x+2;i<=m;i++){
                    if (a[i]==1) j--;
                    if (a[i]==2) j++;
                    if (j>0&&!p1) p1=i,j--;
                    if (j>0&&p1){p2=i;break;}
                }else
                for (j=0,i=x-1;i;i--){
                    if (a[i]==1) j++;
                    if (a[i]==2) j--;
                    if (j>0&&!p2) p2=i,j--;
                    if (j>0&&p2){p1=i;break;}
                }
                a[p1]=1;a[p2]=2;up(no.x,gx(x,0,0),an,1);
            }
        }
        printf("%d\n",ans);
    }
}
/*第二种方法*/
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int i;
struct na{
    int x,z;
    na(int xx,int zz):x(xx),z(zz){}
};
int n,m,x,y,z,a[21],k,en,u=0,p1,p2;
bool map[21][21];
int f[2][1277148],ans;
int v[2][1277148];
int re[21][21];
queue <na> q;
inline int gx(int x,int q1,int q2){k=0;for (register int i=m+1;i;i--) k=k*3+(i==x?q1:(i==x+1?q2:a[i]));return k;}
inline void up(int x,int z,int lj,bool la){
    if (la) lj+=re[x/m+1][x%m+1];
    x++;
    k=x%2;
    if (v[k][z]!=x) v[k][z]=x,f[k][z]=lj,q.push(na(x,z));
    if (lj>f[k][z]) f[k][z]=lj;
}
int main(){
    register int i,j;
    while(scanf("%d%d",&n,&m)!=EOF){
        u++;
        ans=0;
        memset(map,0,sizeof(map));memset(v,0,sizeof(v));memset(f,0,sizeof(f));
        en=n*m-1;
        for (i=1;i<=m;i++)
        for (j=1;j<=n;j++)
        map[i][j]=1;
        for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
        scanf("%d",&re[i][j]);
        if (n==1&&m==1){
            printf("Case %d: %d\n",u,re[1][1]);
            continue;
        }
        f[0][1]=0;
        v[0][0]=1;
        q.push(na(0,1));
        while(!q.empty()){
            na no=q.front();q.pop();
            int an=f[no.x%2][no.z];
            if (no.x%m==0) no.z*=3;
            x=no.x%m+1;y=no.x/m+1;
            for (i=1;i<=m+1;i++) a[i]=0;
            for (i=1,j=no.z;j;i++,j/=3) a[i]=j%3;
            if (no.x==en){
                k=0;
                for (i=1;i<=m+1;i++) k+=a[i]!=0;
                if (k==1&&(a[m]==1||a[m+1]==1)&&an+re[n][m]>ans) ans=an+re[n][m];
                continue;
            }
            if (a[x]==2&&a[x+1]==1){
                up(no.x,gx(x,0,0),an,1);
            }else if (a[x]==0&&a[x+1]==0){
                up(no.x,gx(x,0,0),an,0);
                if (map[x][y+1]&&map[x+1][y])
                up(no.x,gx(x,1,2),an,1);
            }else if (a[x]==0){
                if (map[x+1][y]) up(no.x,gx(x,0,a[x+1]),an,1);
                if (map[x][y+1]) up(no.x,gx(x,a[x+1],0),an,1);
            }else if (a[x+1]==0){
                if (map[x+1][y]) up(no.x,gx(x,0,a[x]),an,1);
                if (map[x][y+1]) up(no.x,gx(x,a[x],0),an,1);
            }else if (a[x]==1&&a[x+1]==1){
                p1=p2=0;
                for (j=0,i=x+2;i<=m+1;i++){
                    if (a[i]==1) j--;
                    if (a[i]==2) j++;
                    if (j>0&&!p1) p1=i,j--;
                    if (j>0&&p1){p2=i;break;}
                }
                a[p1]=1;a[p2]=2;
                up(no.x,gx(x,0,0),an,1);
            }else if (a[x]==2&&a[x+1]==2){
                p1=p2=0;
                for (j=0,i=x-1;i;i--){
                    if (a[i]==1) j++;
                    if (a[i]==2) j--;
                        if (j>0&&!p2) p2=i,j--;
                    if (j>0&&p2){p1=i;break;}
                }
                a[p1]=1;a[p2]=2;up(no.x,gx(x,0,0),an,1);
            }
        }
        printf("Case %d: %d\n",u,ans);
    }
}
时间: 2024-08-11 03:33:03

HDU 3377 Plan的相关文章

【插头DP】HDU 3377 Plan

通道:http://acm.hdu.edu.cn/showproblem.php?pid=3377 题意:简单路径,从左上角走到右下角的最大权值,不需要每个格子都需要经过. 思路:初始化左上角上面那一格有下插头,然后一个不选状态,over. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int MAX_N = 1

HDU 3377 Plan (插头DP,变形)

题意:有一个n*m的矩阵,每个格子中有一个值(可能负值),要从左上角走到右下角,求路径的最大花费. 思路: 除了起点和终点外,其他的点可以走,也可以不走. (2)我用的是括号表示法,所以起始状态为')',即仅有一个右括号,那么到右下角也应该是只有一个右括号.因为,如果碰到()),加粗表示起点的那个右括号,那么合并后变成)##,仍然是右括号,如果是)(),那么合并后变成##),仍然是右括号,相当于延续了.插头每到达一个格子就先将其值给加上,如果要合并的时候,再减掉(因为多算了一次),因此,新括号的

HDU 3377 插头dp

题目大意: 从左上角走到右下角,每个点之多经过一次,取到所有路径上经过点的权值,求最大的权值之和,这里走到右下角就算停止了 这里有个思路是转化成熟悉的回路问题 在上方和右方最外围定义一圈权值为0 , 那么我们相当于从定义以后的左上角开始经过所有外围点形成的回路,那么去掉最外围的0,剩下的就是(1,1)->(n,n)的权值和了 但是这里最外围的点的插头具有特殊性,每次到最外围的点,你左和上有且仅有一个插头,而且出去的方向必然是下一个最外围的点 1 #include <cstdio> 2 #

插头与轮廓线与基于连通性状态压缩的动态规划

问题定义 什么是插头DP 在一个n*m的棋盘上(n与m很小),求: 有多少种不同的回路数 用1条回路经过所有点的方案数 用1条回路经过部分点的方案数 1条路径上的权值和最大 的这一类问题,通常可以用插头DP来解决. 这类问题通常很明显,但代码量大又容易出错,有时TLE有时MLE. 什么是基于状态压缩的动态规划 基于状态压缩的动态规划问题是一类以集合信息为状态且状态总数为指数级的特殊的动态规划问题. 在状态压缩的基础上,有一类问题的状态中必须要记录若干个元素的连通情况,我们称这样的问题为基于连通性

插头DP专题

建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议先理解“插头”的概念.然后会HASH表(这个其实是很基础的东西,应该都会的).然后就是DP. 以及特殊题目的特殊处理. 好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数. 大体上每个题说几句特殊处理,有问题请纠正....题目的顺序基本上难度递增 另外代码我都是用括号匹配的.因为感觉连通

补题清单

HDU 3585 HDU 1693 URAL 1519 FZU 1977 HDU 1964 HDU 3377 POJ 1739 POJ 3133 BZOJ 1025 HDU 4285 专题7 1003 1004 1005 1006 1008

8xFYBh396辗徊肚潘哟约负焕迫厩骄mfdou

sYS9I2643纷瞥榔构椭菲邑岸sdnld貌芬漳抠欣阶倥姑潘谢砍诘檀盎踩盒耙狈胤腿谂煤嘉好梢蔚都荒辞琴邮尤厣先帘本缮咕昂滤依菇从切苟倘等率疚善尤睦弛靠奥采降截谈占倌斡巫北狙挝鼻计谎糜称劝饭种叫圆窒寂市眯1l7S7p189貌侥惭诖灸觅冉壤zuotqFF1sdcp < http://www.cnblogs.com/ztchl/p/8411637.html > < http://www.cnblogs.com/lumberw/p/8411636.html > < http://w

hdu 5290 Bombing plan(树形dp)

题目链接:hdu 5290 Bombing plan dpDestroy[u][i]表示以u为根节点的子树全部被摧毁,并且向上还可以破坏到距离u为i的城市:dpSafe[u][i]表示以u为根节点的子树中有距离u深度为i的城市还未被破坏. dpDestroy[u][i] = dpDestroy[v][i+1] + sum{ min(dpDestroy[k][j], dpSafe[k][j])(j≤i)| k为除了v以外的子节点} dpSafe[u][i] = dpSafe[v][i-1] + s

HDU 3080 The plan of city rebuild(prim和kruskal)

The plan of city rebuild Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 616    Accepted Submission(s): 215 Problem Description News comes!~City W will be rebuilt with the expectation to become