[考试]20150816
1、前言
同样是搜索题,感觉今天的鬼畜多了,还出现了一道标程都莫名其妙的题目。。。
2、Maze 迷宫
大概题意:在一个0-1矩阵中给出起始点和终点,求最短路径。(0为可走,1为不可走)
总结:边界条件出了问题导致WA+RE了20分。我们可以把外围所有点设置为1(不可走),也可以在搜索时添加判断。还有一个很重要的问题,2000*2000的数据在读入时极有可能会超时,建议用读入优化。
题解:最短路径推荐直接BFS。
代码:
-----------------------------------------------------------------------------------------------------
#include<cstdio>
#include<cstdlib>
#define MAXN 2005
#define INF 1<<30
int max(int a,int b) { return (a>b)?a:b; }
struct Queue
{
int x,y;
};
Queue q[MAXN*MAXN];
const int vx[5]={0,-1,1,0,0};
const int vy[5]={0,0,0,-1,1};
int map[MAXN][MAXN],n,m,sx,sy,tx,ty;
int check(int now)
{
if (q[now].x==tx && q[now].y==ty) printf("%d",map[q[now].x][q[now].y]-1),exit(0);
}
void BFS()
{
int head=1,tail=2; q[head].x=sx,q[head].y=sy,map[sx][sy]=1;
while (head!=tail)
{
int nx=q[head].x,ny=q[head].y;
for (int i=1;i<=4;i++)
if (map[nx+vx[i]][ny+vy[i]]==0)
{
map[nx+vx[i]][ny+vy[i]]=map[nx][ny]+1;
q[tail].x=nx+vx[i],q[tail].y=ny+vy[i];
check(tail),tail++;
}
head++;
}
}
int main()
{
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&map[i][j]);
map[i][j]=(!map[i][j])?0:-1;
}
for (int i=1;i<=max(n,m);i++) map[0][i]=map[i][0]=map[n+1][i]=map[i][m+1]=-1;
scanf("%d %d %d %d",&sx,&sy,&tx,&ty);
BFS();
printf("No Answer!");
return 0;
}
-----------------------------------------------------------------------------------------------------
3、Walk 走路
大概题意:现有一个无限大的平面,给出n中行走方式,(dx,dy)表示可以从当前位置(x,y)走到(x+dx,y+dy),求出这些行走方式能否遍历到所有位置。
总结:这是一道略让人无语的题目吧,方式多种多样,我写了一个根据读入数据最大值作为全遍历判断标准的贪心,但是只A了20分,WA了20分。看了标程,直接默认200*200作为判断依据;也有两个AC爷直接根据(-1,0),(1,0),(0,1),(0,-1)四个点来判断,默认最多走15步。这道题可以说并没有什么技巧可言,起初我以为又是要证明什么结论来确定范围。
题解:BFS,具体的见总结。
代码(from YMDragon):
-----------------------------------------------------------------------------------------------------
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
int n,x[15],y[15],ans;
bool check(int x,int y)
{
if ((x==1)&&(y==0)) ans|=1;
if ((x==0)&&(y==1)) ans|=2;
if ((x==-1)&&(y==0)) ans|=4;
if ((x==0)&&(y=-1)) ans|=8;
if (ans==15) return 1; return 0;
}
bool dfs(int j,int X,int Y,int st)
{
if (j>n) return check(X,Y);
for (int i=0; i<=st; i++)
if (dfs(j+1,X+i*x[j],Y+i*y[j],st-i)) return 1;
return 0;
}
void work()
{
scanf("%d",&n);
if (!n) exit(0);
for (int i=1; i<=n; i++) scanf("%d %d",&x[i],&y[i]);
ans=0;
dfs(1,0,0,15);
if (ans==15) printf("YES\n"); else printf("NO\n");
}
int main()
{
freopen("walk.in","r",stdin);
freopen("walk.out","w",stdout);
while (1) work();
return 0;
}
-----------------------------------------------------------------------------------------------------
4、Chess 下棋
大概题意:给出一个初始的4*4的O-X棋盘,当且仅当存在棋子四连时为胜利。X玩家想知道自己现在是否有必胜策略,以及最小字典序走法。
总结:考场上完全没做出来,zzd说和SG游戏相关,其实核心依旧是搜索。
题解:首先需要对于当前棋盘的状态进行压缩,大致思想在于,如果当前状态的后继状态存在某一方必胜的状态,那么该方为了胜利必将选择这条转移道路;由于我们需要X玩家的必胜策略,所以把O玩家的必胜看成X玩家的必败,进行DFS即可。
代码(from z123z123d,有修改):
-----------------------------------------------------------------------------------------------------
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define MAXN 1000005
#define MOD 1000007
using namespace std;
char x,ch[8][8];
int n,m,s[20],go[MAXN],start,vis[MAXN];
int check (int *a)
{
if (a[0]==2 && a[5]==2 && a[10]==2 && a[15]==2) return 0;
if (a[0]==1 && a[5]==1 && a[10]==1 && a[15]==1) return -1;
if (a[3]==2 && a[6]==2 && a[9]==2 && a[12]==2) return 0;
if (a[3]==1 && a[6]==1 && a[9]==1 && a[12]==1) return -1;
for (int i=0,f;i<16;i+=4)
{
f=0;
for (int j=0;j<4;j++)
if (!a[i+j] || (f && a[i+j]!=f)) { f=0; break; } else f=a[i+j];
if (f) return (f==2)?0:-1;
}
for (int i=0,f;i<4;i++)
{
f=0;
for (int j=0;j<16;j+=4)
if (!a[i + j] || (f && a[i+j]!=f)) { f = 0; break; } else f=a[i+j];
if (f) return (f==2)?0:-1;
}
return -2;
}
int work (int *a)
{
int ans=0;
for (int i=15;i>=0;i--) ans=ans*3+a[i];
return ans;
}
void rework (int *a,int &s1,int &s2,int num)
{
for (int i=0;i<16;i++) a[i]=num%3,num/=3,s1+=a[i]==1,s2+=a[i]==2;
}
void dfs(int now)
{
if (vis[now%MOD]) return; vis[now%MOD]=1;
int nt[16]={0},s1=0,s2=0,t;
rework(nt,s1,s2,now);
t=check(nt);
go[now%MOD]=(s1==s2)?-1:0;
if (t!=-2) { go[now%MOD]=t; return; }
for (int i=0;i<16;i++)
if (!nt[i])
if (s1<s2)
{
nt[i]=1,t=work(nt),dfs(t),nt[i]=0;
if (go[t%MOD]==-1) { go[now%MOD]=-1; return; }
}
else
{
nt[i]=2,t=work(nt),dfs(t),nt[i]=0;
if (go[t%MOD]>-1) { go[now%MOD]=i; return; }
}
}
int main()
{
freopen("chess.in","r",stdin);
freopen("chess.out","w",stdout);
for (;;)
{
cin>>x;
if (x=='$') break;
for (int i=1;i<=4;i++) scanf("%s",ch[i]+1);
for (int i=1;i<=4;i++)
for (int j=1;j<=4;j++)
s[4*(i-1)+j-1]=(ch[i][j]=='.')?0:(ch[i][j]=='o')?1:2;
start=work(s);
memset(vis,0,sizeof(vis));
dfs(start);
if (go[start%MOD]!=-1) printf("(%d,%d)\n",go[start%MOD]/4,go[start%MOD]%4);
else printf ("#####\n");
}
return 0;
}
-----------------------------------------------------------------------------------------------------
5、MC 最大团
大概题意:求图的最大团,以及其个数。
题解:待补充。
代码:
-----------------------------------------------------------------------------------------------------
#include <cstdio>
#define MAXN 55
int map[MAXN][MAXN],n,m,ans,tot,max[MAXN],s[MAXN],x,y;
void clear(int d) { for (int i=1;i<=n;i++) if (s[i]==d) s[i]--; }
void dfs(int now,int nowAns)
{
int k=0;
for (int i=now+1;i<=n;i++) if ((map[now][i]) && (s[i]==nowAns-1)) s[i]=nowAns,k++;
if (k)
{
for (int i=now+1;i<=n;i++)
if (s[i]==nowAns)
{
if ((nowAns+k<ans) || (nowAns+max[i]<ans)) break;
dfs(i,nowAns+1),k--,s[i]--;
}
clear(nowAns);
}
else
{
if (nowAns>ans) ans=nowAns,tot=1;
else if (nowAns==ans) tot++;
}
}
int main()
{
freopen("mc.in","r",stdin);
freopen("mc.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=m;i++) scanf("%d %d",&x,&y),map[x][y]=1,map[y][x]=1;
for (int i=n;i>=1;i--)
{
for (int j=i;j<=n;j++) s[j]=0;
dfs(i,1); max[i]=ans;
}
printf("%d %d",ans,tot);
return 0;
}
-----------------------------------------------------------------------------------------------------