【那23个路口】
时间限制:1s
空间限制:32M
先说说现有的两种思路,第一个是动态规划,好像类似于传纸条那个题的多维做法,我不是很了解。第二种思路就是深度优先搜索,如果朴素暴搜的话是4^23种路进行比较,所以需要用到记忆化搜索。
记忆化搜索就是将你已经完成的步数用一个数组记录下来,当下一次需要完成时只需要调用空间里的数组即可。这是一种以空间换时间的方法。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int i,j,m,n,t,sx,sy;
long longmaxx,endd;//坑1:这里的结果必须是long long,//题目中只给出了每一个输入的元素//都是longint,但没有注明结果取值
int mo[4][2]={{0,1},{0,-1},{-1,0},{1,0}};
int a[301][301];
long longf[24][50][50];//这个题目比较特殊,鉴于题目要//求是23步以内,所以我们需要记//录以出发点为中心23*23的区域,//并记录某一步在某一个点的最大//权值。
int readd()
{
int aans=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
{
aans*=10;
aans+=ch-'0';
ch=getchar();
}
return aans;
//读入优化,注意处理负数,这里没有处理负数的原因是下面//的输入好像没有用读入优化
void dfs(int x,int nx,int ny,long long now,int zx,int zy)//解释一下变量的含义:x—已走步数
//nx&ny—当前位置的横纵坐标
//now—当前位置获得的最大权值,注意开long long
//zx&zy—我们所编辑的坐标位置(以出发点为原点)
{
if(a[nx][ny]>0||(nx==sx&&ny==sy))//可行性剪枝
{
now+=a[nx][ny];
if(f[x][zx][zy]<now||(nx==sx&&ny==sy))//记忆化
f[x][zx][zy]=now;
else return; //剪枝
if(x==23){maxx=max(maxx,now);return;}
dfs(x+1,mo[0][0]+nx,mo[0][1]+ny,now,mo[0][0]+zx,mo[0][1]+zy);
dfs(x+1,mo[1][0]+nx,mo[1][1]+ny,now,mo[1][0]+zx,mo[1][1]+zy);
dfs(x+1,mo[2][0]+nx,mo[2][1]+ny,now,mo[2][0]+zx,mo[2][1]+zy);
dfs(x+1,mo[3][0]+nx,mo[3][1]+ny,now,mo[3][0]+zx,mo[3][1]+zy);
//四个方向大法师
}
}
int main()
{
//freopen("corner.in","r",stdin);
//freopen("corner.out","w",stdout);
//freopen("corner2.in","r",stdin);//。。。
n=readd(),m=readd();
for(i=1;i<=m;i++)
for(j=1;j<=n;j++)
{
cin>>a[i][j];
if(!a[i][j])
sx=i,sy=j;
}
dfs(0,sx,sy,0,25,25);//初始化
cout<<maxx;
}
【我们的可可西里】
这个题类似于八皇后问题,童鞋们都用了大法师,具体大法师的代码如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int i,j,m,n,t;
int l[1005];
int readd()
{
int aans=0;
char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9')
{
aans*=10;
aans+=ch-'0';
ch=getchar();
}
return aans;
}
//加了读入优化也无济于事
void dfs(int x)
{
if(x==n+1) {t++;return;}
for(int i=1;i<=n;i++)
{
if(!l[i]&&i!=x)
{
l[i]=1;
dfs(x+1);
l[i]=0;
}
}
}
int main()
{
//freopen("keke.in","r",stdin);
//freopen("keke.out","w",stdout);
n=readd();
dfs(1); //DFS暴搜
cout<<t;
return 0;
}
显然这个题绝对不能这么做,因为一旦到了N=11的时候就已经1.04秒上下了,所以我们只能得30分。
然后通过打表我们发现规律:
对于a[i]这个数组,我们有a[i]=a[i-1]*(i-1)*(-1)^(i-1)
所以这个题就很容易了吗?不,我们来看看当N=100的时候这个数有多大吧:
34332795984163804765195977526776142032365783805375784983543400282685180793327632432791396429850988990237345920155783984828001486412574060553756854137069878601
显然unsigned long long都不行,所以就得用高精了,为了节省时间,我们发现总是一个高(低)精数乘以一个低精数,所以我们就可以压位,就是把低精压在一个位上去做。
如下代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int i,j,m,n;
int s1[10005];
int mm[10005];
long long b;
void mul(int y)
{
j=1;
while(j<=s1[0])
{
mm[j]=s1[j]*y+b;//ѹλ
b=mm[j]/10;
mm[j]%=10;
j++;
}
j--;
while(b)
{
mm[j+1]+=b%10;
b/=10;
j++;
}
mm[0]=j;
}
int main()
{
s1[1]=1;
s1[0]=1;
cin>>n;
if(n==3)
cout<<2;
else
{for(i=4;i<=n+1;i++)
{
mul((i-1));
if(i%2==0)
mm[1]--;
else
mm[1]++;
int tem=1,x;
while(mm[tem]>10)
{
x=mm[tem]/10;
mm[tem]%=10;
tem++;
mm[tem]+=x;
}
for(j=1;j<=mm[0];j++)
s1[j]=mm[j];
s1[0]=mm[0];
}
for(i=s1[0];i>=1;i--)
cout<<s1[i];
}
}