/*
题意:这个题目的意思是给定你起点S,和终点D,问你是否能在 T 时刻恰好到达终点D。
分析:这样一看很明显是DFS,不过里面涉及到很多剪枝。
奇偶剪枝:
是数据结构的搜索中,剪枝的一种特殊小技巧。
现假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,
s
|
|
|
+ — — — e
如图所示(“|”竖走,“—”横走,“+”转弯),
易证abs(ex-sx)+abs(ey-sy)为此问题类中任意情况下,起点到终点的最短步数,记做step,
此处step1=8;
s — — —
— — +
| +
|
+ — — — e
如图,为一般情况下非最短路径的任意走法举例,step2=14;
step2-step1=6,偏移路径为6,偶数(易证);
故,若t-[abs(ex-sx)+abs(ey-sy)]结果为非偶数(奇数),则无法在t步恰好到达;
返回,false;
反之亦反。
心得:起初没仔细看题,以为是 T 时间内到达的,果断 BFS 呀,于是果断 WA ,
于是就把题目在仔细看了一遍,没想到是 T 时刻到达,
所以心凉了一截,再次果断 DFS ,稍微剪下枝,就交了TLE,无语了,
想了好久没不知道怎么剪枝的,后来看了下解题报告才知道还有个奇偶剪枝,挺强大的。
以后要仔细看题
*/
#include<iostream>
#include<cstring>
#define N 10
using namespace std;
int n,m,t,end_i,end_j;
bool visited[N][N],flag,ans;
char map[N][N];
int abs(int a,int b)
{
if(a<b) return b-a;
else return a-b;
}
void DFS(int i,int j,int c)
{
if(flag) return ;
if(c>t) return ;
if(i<0||i>=n||j<0||j>=m) {return ;}
if(map[i][j]=='D'&&c==t) {flag=ans=true; return ;}
int temp=abs(i-end_i)+abs(j-end_j);
temp=t-temp-c;
if(temp&1) return ;//奇偶剪枝
if(!visited[i-1][j]&&map[i-1][j]!='X')
{
visited[i-1][j]=true;
DFS(i-1,j,c+1);
visited[i-1][j]=false;
}
if(!visited[i+1][j]&&map[i+1][j]!='X')
{
visited[i+1][j]=true;
DFS(i+1,j,c+1);
visited[i+1][j]=false;
}
if(!visited[i][j-1]&&map[i][j-1]!='X')
{
visited[i][j-1]=true;
DFS(i,j-1,c+1);
visited[i][j-1]=false;
}
if(!visited[i][j+1]&&map[i][j+1]!='X')
{
visited[i][j+1]=true;
DFS(i,j+1,c+1);
visited[i][j+1]=false;
}
}
int main()
{
int i,j,x,y,k;
while(cin>>m>>n>>t&&(m||n||t))
{
memset(visited,false,sizeof(visited));
k=0;
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
cin>>map[i][j];
if(map[i][j]=='S')
{
x=i;y=j;
visited[i][j]=true;
}
if(map[i][j]=='D')
{
end_i=i;end_j=j;
}
if(map[i][j]=='X')k++;
}
}
ans=flag=false;
if(n*m-k-1>=t) DFS(x,y,0);
if(ans) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
Prime Ring Problem
#include<iostream>
#define N 25
#define M 40
using namespace std;
bool is_prime[M],visited[N];
int n,test,ans[N];
void work(int k)
{
int i;
if(k==n+1)
{
if(!is_prime[ans[n]+ans[1]]) return ;
for(i=1;i<=n-1;i++)
cout<<ans[i]<<" ";
cout<<ans[i]<<endl;
return ;
}
for(i=2;i<=n;i++)
{
if(!visited[i]&&is_prime[ans[k-1]+i])
{
visited[i]=true;
ans[k]=i;
work(k+1);
visited[i]=false;
}
}
}
bool prime(int n)
{
if(n==1) return false;
if(n==2||n==3) return true;
int i;
for(i=2;i<n;i++)
if(n%i==0)
return false;
return true;
}
int main()
{
int i;test=1;
for(i=1;i<M;i++) is_prime[i]=prime(i);
while(cin>>n)
{
ans[1]=1;
memset(visited,false,sizeof(visited));
cout<<"Case "<<test<<":"<<endl;
work(2);
test++;
cout<<endl;
}
return 0;
}
蜘蛛牌
#include <iostream>
#include <queue>
using namespace std;
struct Node{
int min;
int max;
};
int num[11];
int used[11];
int ans;
void DFS(int rank,int sum){
if(sum > ans){//剪枝
return;
}
if(rank == 9){//递归出口
ans = sum;
return;
}
int i,j;
for(i=1;i<=10;i++){
if(!used[i]){//枚举每一种可能
for(j=i+1;j<=10;j++){//这个for判断枚举的可能性是否合法
if(!used[j]){//判断1时,发现2,3已被使用说明2,3都被放在了4的位置上
used[i] = 1;
DFS(rank+1,sum+abs(num[i]-num[j]));
break;
}
}
used[i] = 0;
}
}
}
int main(){
int T;
cin>>T;
while(T--){
int i;
for(i=1;i<=10;++i){
int x;
cin>>x;
num[x] = i;/*用下标表示每个数字,数值记录位置
数字则变成升序的排列
在堆叠时若发现右边已经被使用则所要的卡牌必然再更后面*/
}
ans = 0x3fffffff;
DFS(0,0);
cout<<ans<<endl;
}
return 0;
}
Remainder
#include<stdio.h>
#include<string.h>
#define N 1000001
/*
简单数论+bfs的题
WA了一上午,关键在于要对k*m取模来保存状态信息。
*/
struct node{
int r;
int pre;
char op;
}q[N];
int n,k,m,vis[N],km;
int mode(int t,int i)
{
switch (i){
case 0:
return ((t+m)%km+km)%km;
case 1:
return ((t-m)%km+km)%km;
case 2:
return ((t*m)%km+km)%km;
case 3:
return (t%m+m)%m%km;
}
}
char f[5]="+-*%";
int bfs()
{
memset(vis,0,sizeof(vis));
int i,t,p,pre,front,rear,goal;
goal=((n+1)%k+k)%k;
front=rear=0;
t=n;
// if(n==goal)//这里不考虑初始就相等也可以AC
// return -1;
for(i=0;i<4;++i)
{
p=mode(t,i);
vis[p]=1;
q[rear].op=f[i];
q[rear].r=p;
q[rear].pre=-1;
p=p%k;
if(p==goal)
return rear;
rear++;
}
while(front<rear)
{
pre=front;
t=q[front++].r;
for(i=0;i<4;++i)
{
p=mode(t,i);
if(!vis[p])
{
vis[p]=1;
q[rear].op=f[i];
q[rear].r=p;
q[rear].pre=pre;
p%=k;
if(p==goal)
return rear;
rear++;
}
}
}
return -1;
}
void solve()
{
int s=bfs();
if(s<0)
{
printf("0\n");
return ;
}
int i=0;
char str[N];
while(s!=-1)
{
str[i++]=q[s].op;
s=q[s].pre;
}
printf("%d\n",i);
--i;
while(i>0)
printf("%c",str[i--]);
printf("%c\n",str[i]);
}
int main()
{
while(scanf("%d%d%d",&n,&k,&m)==3&&(n||k||m))
{
km=k*m;
solve();
}
return 0;
}
Sudoku Killer
#include<stdio.h>
int map[9][9],kong[90][2],kn;
int ci=0,f;
int he(int i,int j,int k)//判断能填k不能
{
int o,p,oo,pp;
for(o=0;o<9;o++)
if(map[i][o]==k)
return 0;
for(o=0;o<9;o++)
if(map[o][j]==k)
return 0;
oo=i/3*3;
pp=j/3*3;
for(o=oo;o<(oo+3);o++)
for(p=pp;p<(pp+3);p++)
if(map[o][p]==k)
return 0;
return 1;
}
void shu()//输出
{
int i,j;
f=1;
if(ci!=1)
printf("\n");
for(i=0;i<9;i++)
{
printf("%d",map[i][0]);
for(j=1;j<9;j++)
printf(" %d",map[i][j]);
printf("\n");
}
}
void dfs(int l)
{
int k;
if(l<kn)
{
for(k=1;k<10;k++)//依次试试kong[l]该赋何值
{
if(he(kong[l][0],kong[l][1],k))
{
map[kong[l][0]][kong[l][1]]=k;
dfs(l+1);
if(f) return;
map[kong[l][0]][kong[l][1]]=0;
}
}
}
else
shu();
}
int main()
{
char c;
int i,j;
while(1)
{
kn=0;
for(i=0;i<9;i++)
for(j=0;j<9;j++)
{
scanf("%c",&c);
if(c=='?')
{
map[i][j]=0;
kong[kn][0]=i;//把是?的坐标记录到kong[]中,以便后面依次赋值
kong[kn][1]=j;
kn++;
}
else map[i][j]=c-'0';
getchar();
}
ci++;
f=0;
dfs(0);//从kong[0]开始赋值
i=scanf("%c",&c);
if(i==-1)//若不能读到字符,跳出
break;
}
return 0;
}
N皇后问题
/**
n皇后问题,由于N 是小于等于10的正整数,
所以可以使用打表的方法把前十中情况全
部找出来存起来然后每次输入时不用重新
的计算了。
*/
#include <stdio.h>
#define NUMS 10
/*输入的数字1---10*/
int N;
/*棋盘*/
int chessboard[11][11];
/* 用来记录拜访数目 */
int cal;
/*
检查皇后放置此行此列是否可以,可以返回1,不可以返回0
此递归是一行一行找的,K是棋盘的长度
*/
int dfs_check(int row,int column,int k)
{
/* 说明已经到了棋盘的界外,前边都符合了 */
if(row>k)
{
cal++;
return 1;
}
/* 正上方是否有皇后*/
for(int i = 1; i < row; i++)
/* 如果有皇后则返回不能放置这里返回0*/
if(chessboard[i][column] == 1)
return 0;
/* 左右上方45度角检查是否可以*/
/* 左上方*/
for(int i=row-1,j=column-1;i>0&&j>0;i--,j--)
if(chessboard[i][j] == 1)
return 0;
/* 右上方*/
for(int i=row-1,j=column+1;i>0&&j<=k;i--,j++)
if(chessboard[i][j] == 1)
return 0;
/*标记这个位置成功了*/
chessboard[row][column] = 1;
/*进行下一行搜索*/
for(int i=1;i<=k;i++)
if(dfs_check(row+1,i,k)==1)
break;
chessboard[row][column] = 0;
return 0;
}
int main()
{
int i,j,k;
int count[11];
/*打表*/
for(k=1;k<=NUMS;k++)
{
count[k] = 0;
cal = 0;
/*首先将棋盘初始化全部置为0*/
for(i=0;i<=NUMS;i++)
for(j=0;j<=NUMS;j++)
chessboard[i][j]=0;
for(i=1;i<=k;i++)
dfs_check(1,i,k);
count[k] = cal;
}
while(scanf("%d",&N)!=EOF&&N!=0)
printf("%d\n",count[N]);
return 0;
}
The magic apple tree
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int maxn=20001;
vector<int> g[maxn];
int in[maxn], out[maxn];
int dfs(int root){
if(out[root]==0)
return root;
vector<int> tmp;
for(int i=0;i<out[root];i++)
tmp.push_back(dfs(g[root][i]));
sort(tmp.begin(),tmp.end());
return tmp[(out[root]+1)/2-1];
}
char c;
inline void scan(int &x){
while(c=getchar(),c<'0'||c>'9');
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';
}
int main()
{
int N,tmp,i,j,root;
while(~scanf("%d",&N))
{
for(i=1;i<=N;i++)
g[i].clear();
memset(in,0,sizeof(in));
for(i=1;i<=N;i++)
{
scan(out[i]);
for(j=1;j<=out[i];j++)
{
scan(tmp);
g[i].push_back(tmp);
in[tmp]++;
}
}
for(i=1;i<=N;i++)
{
if(in[i]==0)
{
root=i;
break;
}
}
printf("%d\n",dfs(root));
}
return 0;
}
Fling
/*
这题题意是一个弹来弹去的游戏。
在一个7*8的板子上,有若干个球(小于12个,经测试最多11个)。
你每次可以选择一个球向上下左右推动,能推动的条件是推动的方向上有球但是不能粘在一起,
中间必需得隔一个及以上的格子。
然后你推动这个球后它会一直在这个方向上滚动,直到碰到下一个球或者掉下板子去。
如果碰到下一个球他的动能会传递下去,如果碰到的球紧挨着另一个球就隔山打牛,
而原来的球就停在碰到的球的前一个位置上。
然后结束标志是板子上只剩一个球。
输出每次操作的球的坐标和推动的方向(ULRD)。
如果有多种情况就按照越左上角越好和U,L,R,D的优先级输出优先级最高的情况。
起初是想记录下所有的O点保存进结构体,这样就一定满足左上角在前来进行搜索了。
然后把ULRD编作1234进行分类讨论,当搜到满足条件的情况时输出的自然就是优先级最高的解了。
实际上这样也是可行的,但是中途卡了一段时间导致我重新换了思路。
最后我是每次DFS都把地图搜一遍先搜到的先进行操作,结果是一样的。但是可能会慢点。
思路是很简单,但这题烦在回溯和Debug上………………
*/
#include <cstdio>
#define MAX 8
using namespace std;
char map[7][MAX];
char s[11][MAX];
int n,flag,total;
struct node{
int i,j;
int run;
}road[11];
bool judge(int curi,int curj,int x) //判断是否可推函数,长了Debug好痛苦。。。
{
int i;
if(x==1){
if(curi-1<0||(map[curi-1][curj]=='O'))
return 0;
for(i=2;curi-i>=0;i++)
if(map[curi-i][curj]=='O')
return 1;
}else if(x==2){
if(curj-1<0||(map[curi][curj-1]=='O'))
return 0;
for(i=2;curj-i>=0;i++)
if(map[curi][curj-i]=='O')
return 1;
}else if(x==3){
if(curj+1>=8||(map[curi][curj+1]=='O'))
return 0;
for(i=2;curj+i<8;i++)
if(map[curi][curj+i]=='O')
return 1;
}else if(x==4){
if(curi+1>=7||(map[curi+1][curj]=='O'))
return 0;
for(i=2;curi+i<7;i++)
if(map[curi+i][curj]=='O')
return 1;
}
return 0;
}
void dfs(){
int i,j,k,l,l2;
if(total==n){
for(i=1;i<total;i++){
printf("%d %d ",road[i].i,road[i].j);
if(road[i].run==1)
printf("U\n");
else if(road[i].run==2)
printf("L\n");
else if(road[i].run==3)
printf("R\n");
else if(road[i].run==4)
printf("D\n");
}
flag=1;
return ;
}
for(i=0;i<7;i++){
for(j=0;j<8;j++){
if(map[i][j]=='O'){
for(k=1;k<=4;k++){
if(!judge(i,j,k))continue;
road[total].i=i;
road[total].j=j;
road[total].run=k;
if(k==1||k==4){
for(l=0;l<7;l++){
s[total][l]=map[l][j]; //该列改变了,记录该列。
}
}
else{
for(l=0;l<8;l++){
s[total][l]=map[i][l]; //该行改变了,记录该行。
}
}
map[i][j]='X';
if(k==1){ //分类讨论推动方向
l2=i;
for(l=i-1;l>=0;l--){
if(map[l][j]=='O'){
map[l+1][j]='O';
if(l2!=l+1){
map[l2][j]='X';
}
l2=l;
}
}
map[l2][j]='X';
}else if(k==2){
l2=j;
for(l=j-1;l>=0;l--){
if(map[i][l]=='O'){
map[i][l+1]='O';
if(l2!=l+1){
map[i][l2]='X';
}
l2=l;
}
}
map[i][l2]='X';
}else if(k==3){
l2=j;
for(l=j+1;l<8;l++){
if(map[i][l]=='O'){
map[i][l-1]='O';
if(l-1!=l2){
map[i][l2]='X';
}
l2=l;
}
}
map[i][l2]='X';
}else if(k==4){
l2=i;
for(l=i+1;l<7;l++){
if(map[l][j]=='O'){
map[l-1][j]='O';
if(l-1!=l2){
map[l2][j]='X';
}
l2=l;
}
}
map[l2][j]='X';
}
total++;
dfs();
if(flag)return ;
total--;
if(k==1||k==4){ //进行回溯。
for(l=0;l<7;l++){
map[l][j]=s[total][l];
}
}
else{
for(l=0;l<8;l++){
map[i][l]=s[total][l];
}
}
}
}
}
}
}
int main()
{
int casi=0;
int i,j;
while(scanf("%s",s[0])!=EOF){
if(casi)printf("\n");
n=0;
for(i=0;i<8;i++){
map[0][i]=s[0][i];
if(map[0][i]=='O')
n++;
}
for(i=1;i<7;i++){
scanf("%s",s[0]);
for(j=0;j<8;j++){
map[i][j]=s[0][j];
if(map[i][j]=='O')
n++;
}
}
flag=0;
total=1;
printf("CASE #%d:\n",++casi);
dfs();
}
return 0;
}