洛谷 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…
似乎是再想办法优化一下就能过的
没想到小小的优化能有那么大效果
也想不出优化了啊