ACM Steps_Chapter Four_Section3

Tempter of the Bone

/*
题意:这个题目的意思是给定你起点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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值