洛谷 P1123 取数游戏

洛谷 P1123 取数游戏
题目描述

一个N×M的由非负整数构成的数字矩阵,你需要在其中取出若干个数字,使得取出的任意两个数字不相邻(若一个数字在另外一个数字相邻8个格子中的一个即认为这两个数字相邻),求取出数字和最大是多少。

输入输出格式
输入格式:

第1行有一个正整数T,表示了有T组数据。

对于每一组数据,第一行有两个正整数N和M,表示了数字矩阵为N行M列。

接下来N行,每行M个非负整数,描述了这个数字矩阵。

输出格式:

T行,每行一个非负整数,输出所求得的答案。


dfs搜过去
…就TLE了


首先最好想的 八方标记 然后接着刚搜到的地方继续搜
83分代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,n,m;
int ans,xx,yy,flag;
int a[10][10],b[10][10];
int x1[6]={0,0,0,1,1,1};//自己也要标记一下... 
int y1[6]={-1,0,1,-1,0,1};
void vis1(int x,int y)
{
    for(int i=0;i<6;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]++;
    }
}
void vis2(int x,int y)
{
    for(int i=0;i<6;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]--;
    }
}
void dfs(int x,int y,int num)
{
    flag=0;
    for(int i=x;i<n;++i)
    {
        for(int j=0;j<m;++j)
        {
            if(!b[i][j])
            {
                flag=1;
                vis1(i,j);
                dfs(i,j,num+a[i][j]);
                vis2(i,j);
            }
        }
    }
    if(!flag)
    {
        if(num>ans) ans=num;
    }
}
int main(){
    scanf("%d",&t);
    for(int k=0;k<t;++k)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)
            {
                scanf("%d",&a[i][j]);
            }
        }
        memset(b,0,sizeof(b));
        ans=-1;
        dfs(0,0,0);
        printf("%d\n",ans);
    }
    return 0;
}

然后是标记一圈 只搜再外一圈
事实证明是错的
32分代码

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,n,m;
int ans,xx,yy,flag;
int a[10][10],b[10][10];
int x1[9]={0,0,-1,-1,-1,0,1,1,1};//自己也要标记一下... 
int y1[9]={0,-1,-1,0,1,1,1,0,-1};
int x2[16]={0,-1,-2,-2,-2,-2,-2,-1,0,1,2,2,2,2,2,1};
int y2[16]={-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2};
void vis1(int x,int y)
{
    for(int i=0;i<9;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]++;
    }
}
void vis2(int x,int y)
{
    for(int i=0;i<9;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]--;
    }
}
void dfs(int x,int y,int num)
{
    vis1(x,y);
    for(int i=0;i<16;++i)
    {
        xx=x+x2[i];
        yy=y+y2[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m || b[xx][yy]!=0) continue;
        dfs(xx,yy,num+a[xx][yy]);
    }
    vis2(x,y);
    if(num>ans) ans=num;
}
int main(){
    scanf("%d",&t);
    for(int k=0;k<t;++k)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)
            {
                scanf("%d",&a[i][j]);
            }
        }
        memset(b,0,sizeof(b));
        ans=-1;
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)
            {
                dfs(i,j,a[i][j]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

下了数据来查,发现有些点可能搜不到
比如下面这个例子

1 2  3  4
5 6  7  8
9 10 11 12

 1  (2)  3  (4)
(5) (6)  7  (8)
 9  10   11 (12)

只从1开始搜的话
不论后面搜的是3,7,11还是9,10
4,8,12必定被标记,后面就搜不到
但实际上这种组合是可行的

也就是,行数或列数为双数时
此方法不可行

考虑改进 搜索范围扩大一圈
66分(无WA了)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,n,m;
int ans,xx,yy,flag;
int a[10][10],b[10][10];
int x1[9]={0,0,-1,-1,-1,0,1,1,1};//自己也要标记一下... 
int y1[9]={0,-1,-1,0,1,1,1,0,-1};
int x2[40]={0,-1,-2,-2,-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-3,-3,-3,-3,-3,-3,-3,-2,-1,0,1,2,3,3,3,3,3,3,3,2,1};
int y2[40]={-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-3,-3,-3,-3,-2,-1,0,1,2,3,3,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3};
void vis1(int x,int y)
{
    for(int i=0;i<9;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]++;
    }
}
void vis2(int x,int y)
{
    for(int i=0;i<9;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]--;
    }
}
void dfs(int x,int y,int num)
{
    if(num>ans) ans=num;
    vis1(x,y);
    for(int i=0;i<40;++i)
    {
        xx=x+x2[i];
        yy=y+y2[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m || b[xx][yy]!=0) continue;
        dfs(xx,yy,num+a[xx][yy]);
    }
    vis2(x,y);
    return;
}
int main(){
    scanf("%d",&t);
    for(int k=0;k<t;++k)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)
            {
                scanf("%d",&a[i][j]);
            }
        }
        memset(b,0,sizeof(b));
        ans=-1;
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)
            {
                dfs(i,j,a[i][j]);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

最后跑去看正解

1.手动换点换行

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,n,m;
int ans;
//int xx,yy;//xx,yy不能在这儿定义QAQ
int a[10][10],b[10][10];
void dfs(int x,int y,int num)
{
    if(ans<num) ans=num;
    int xx,yy;
    xx=x;
    yy=y+1;
    if(yy>m)
    {
        xx++;
        yy=1;
        if(xx>n) return;
    }
    if(!b[xx][yy-1] && !b[xx-1][yy-1] && !b[xx-1][yy] && !b[xx-1][yy+1])
    {
        b[xx][yy]=1;
        dfs(xx,yy,num+a[xx][yy]);
        b[xx][yy]=0;
    }
    dfs(xx,yy,num);
}
int main(){
    scanf("%d",&t);
    for(int k=0;k<t;k++)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        ans=0;
        b[1][1]=0;
        dfs(1,1,0);
        b[1][1]=1;
        dfs(1,1,a[1][1]);
        printf("%d\n",ans);
    }
    return 0;
}

2.跳过正在搜这行的前半段(这种搜索方法保证是从前往后搜的,搜过的已经没有必要枚举了)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int t,n,m;
int ans,xx,yy,flag;
int a[10][10],b[10][10];
int x1[6]={0,0,0,1,1,1};//自己也要标记一下... 
int y1[6]={-1,0,1,-1,0,1};
//int x2[16]={0,-1,-2,-2,-2,-2,-2,-1,0,1,2,2,2,2,2,1};
//int y2[16]={-2,-2,-2,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2};
void vis1(int x,int y)
{
    for(int i=0;i<6;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]++;
    }
}
void vis2(int x,int y)
{
    for(int i=0;i<6;++i)
    {
        xx=x+x1[i];
        yy=y+y1[i];
        if(xx<0 || xx>=n || yy<0 || yy>=m) continue;
        b[xx][yy]--;
    }
}
void dfs(int x,int y,int num)
{
    flag=0;
    for(int i=y;i<m;++i)
    {
        if(!b[x][i])
        {
            flag=1;
            vis1(x,i);
            dfs(x,i,num+a[x][i]);
            vis2(x,i);
        }
    }
    for(int i=x+1;i<n;++i)
    {
        for(int j=0;j<m;++j)
        {
            if(!b[i][j])
            {
                flag=1;
                vis1(i,j);
                dfs(i,j,num+a[i][j]);
                vis2(i,j);
            }
        }
    }
    if(!flag && num>ans) ans=num;
}
int main(){
    scanf("%d",&t);
    for(int k=0;k<t;++k)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m;++j)
            {
                scanf("%d",&a[i][j]);
            }
        }
        memset(b,0,sizeof(b));
        ans=-1;
        dfs(0,0,0);
        printf("%d\n",ans);
    }
    return 0;
}

然后这题终于是过了T^T…
似乎是再想办法优化一下就能过的
没想到小小的优化能有那么大效果
也想不出优化了啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值