9.14~9.15集训小结

搜索TwT

今天化身WA队金牌剪枝园丁疯狂TLE

 Day1T1:

Day1T1http://118.31.67.228/problem.php?cid=1020&pid=0icon-default.png?t=L892http://118.31.67.228/problem.php?cid=1020&pid=0

样例输入:

5 3

样例输出 :

  1  2  3
  1  2  4
  1  2  5
  1  3  4
  1  3  5
  1  4  5
  2  3  4
  2  3  5
  2  4  5
  3  4  5

题解: 

        三年前可以写出来的题,不会现在写不出来吧,写不来该挨打

#include<cstdio>

using namespace std;

int n,r;
int now[30];
bool vis[30];

inline void dfs(int fath,int depth)
{
	if(depth==r+1){
		for(int i=1;i<depth;i++)printf("%3d",now[i]);
		putchar('\n');
		return ;
	}
	for(int i=fath+1;i<=n;i++){
		if(vis[i]==0){
			vis[i]=1;
			now[depth]=i;
			dfs(i,depth+1);
			vis[i]=0;
			now[depth]=0;
		}
	}
}

int main(void)
{
	scanf("%d%d",&n,&r);
	dfs(0,1);
}

Day1T2:

Day1T2http://118.31.67.228/problem.php?cid=1020&pid=1

样例输入:

4 6
a t c i s w

样例输出 :

acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw

题解: 

        正解说是要:

【知识讲解】

深度优先搜索中的剪枝优化:

    在深度优先搜索的过程中,如果发现以当前状态往下搜索,不可能得到解或者最优解,那么就应该及时“后退”,避免搜索当前结点(状态)下面的“子搜索树”,以减少搜索时间,提高搜索效率。这种思想称为搜索“剪枝”。这跟走迷宫时提前判断出“死胡同”,不走冤枉路一个意思。


【问题分析】

    设len表示读人(要生成)的串长,num表示字符的总个数。因为所有的密码都要按字典序输出,所以对读入的所有字符a[i]按升序排列。为了处理方便,再设b[i]表示数组a中位置i及之后还有几个元音。

    采用深度优先搜索,设dfs(x,y,n1 ,n2,s)表示搜到第x个字符,即将生成的字符串长度为y,至少还要nl个元音和n2个辅音,生成的字符串为s,则可以得到以下代码:


 void dfs(int x,int y,int n1,int n2, string s){

  if(y == len+1){

   输出s;

   计数器加1,如果等于25000就中止程序;

  }

  else if(a[i] 是元音) dfs(x+1,y+1,n1-1,n2,s+a[x]);

  else dfs(x+1,y+1,n1,n2-1,s+a[x]);

 }


    直接调用dfs(1,1,1,2,"")即可。但是,运行程序发现超时严重。下面考虑对递归代码(阴影部分)进行适当的剪枝优化。

    (1)如果接下来的字母全用上但长度也不够,则剪枝。

    (2)如果接下来没有元音且之前没有用过元音,则剪枝。

    (3)如果接下来的辅音全取完再加上之前取的辅音也不够2个,则剪枝。

但是!完全不用

        因为只让我们输出25000个我们只需要枚举出所有排列,输出符合两个辅音一个元音的情况,到25000个的时候直接终止

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

int n,r;
char val[30];
char now[30];
bool vis[30];
int k1,k2;
int cnt;

inline void dfs(int fath,int depth)
{
	if(cnt>=25000)return;
	if(depth==r+1){
		k1=0;k2=0;
		for(int i=1;i<depth;i++){
			if( now[i]=='a'||now[i]=='e'||
				now[i]=='i'||now[i]=='o'||
				now[i]=='u')k1++;
			else k2++;
		}
		if(k1<1 || k2<2)return;
		for(int i=1;i<depth;i++)putchar(now[i]);
		putchar('\n');
		cnt++;
		return ;
	}
	for(int i=fath+1;i<=n;i++){
		if(vis[i]==0){
			vis[i]=1;
			now[depth]=val[i];
			dfs(i,depth+1);
			vis[i]=0;
			now[depth]=' ';
		}
	}
}

int main(void)
{
	scanf("%d%d",&r,&n);
	for(int i=1;i<=n;i++)cin>>val[i];
	sort(val+1,val+1+n);
	cnt=0;
	dfs(0,1);
}

Day1T3:

Day1T3http://118.31.67.228/problem.php?cid=1020&pid=2

样例输入:

5 10
2 2 6 5 4

样例输出 :

2 2 6 

题解: 

        其实最搞人的不是数据太大被卡,最搞人最难受的是题面告诉你数据巨大,事实上不优化一次过,加了优化全部TLE(OIER崩溃的瞬间

        对于这种问题,当n比较小的时候用搜索合适,当c比较小的时候用背包比较合适,但是背包想要输出方案相当麻烦。但是要是数据真的像题面那样ex的话,对于现在的我无法解决。

 2、3、4行都是加了排序后搜索的,超时,1行是啥也不加的暴力中的暴力哭了QAQ

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;

int n,pur;
int val[100000];
int now[30000];
int sum[30000];
bool vis[30000];

template <typename T>inline void in(T &x)
{
	T ch=getchar(),xx=0,fw=1;
	while(!isdigit(ch)){if(ch=='-')fw=-1;ch=getchar();}
	while(isdigit(ch)){xx=(xx<<1)+(xx<<3)+ch-'0';ch=getchar();}
	x=xx*fw;
}

template <typename T>inline void prt(T x)
{
	if(x>9)prt(x/10);
	putchar(x%10+'0');
}

inline void dfs(int fath,int depth,int state)
{
	if(vis[0]==1)return;
	if(sum[n]-sum[fath-1]<pur-state)return;
	if(state==pur){
		for(register int i=1;i<depth;i++)prt(now[i]),putchar(' ');
		putchar('\n');
		vis[0]=1;
		return;
	}
	if(depth==n+1)return;
	for(register int i=1;i<=n;i++){
		if(vis[i]==0 && state+val[i]<=pur){
			vis[i]=1;
			now[depth]=val[i];
			dfs(i,depth+1,state+val[i]);
			vis[i]=0;
			now[depth]=0;
		}
	}
}

inline bool tmp(int xx,int yy){return xx>yy;}

int main(void)
{
	in(n);in(pur);
	for(register int i=1;i<=n;i++)in(val[i]),sum[i]=sum[i-1]+val[i];
	//sort(val+1,val+1+n,tmp);
	dfs(1,1,0);
	if(vis[0]==0)printf("No Solution!");
	return 0;
}

Day1T5:

Day1T5http://118.31.67.228/problem.php?cid=1020&pid=4

样例输入:

.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.

样例输出 :

527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936

题解: 

        和这个该死的蛋疼的题斗智斗勇了一下午,它终于还是低在了我和巨人的脚下

        不bb了,这道题单纯的判断横竖正方形是肯定不行的,这时,我们强大的状态压缩登场了,这道题只有9个数,所以一个511的数字就可以存下所有可能的状态了,比如101011000这个数就表示1、2、3、6、8这五个数字已经被填过了,那我们就把每一行、每一列、每一个正方形的状态压缩出来,然后对于一个位置,将三数&起来然后取lowbit就是这个空可以填的数

        你以为这样就完了吗?NoNoNo

        这样以来只是优化了判断能不能填,我们还有优化每次填那里

        第一次我想的是每一层dfs枚举所有位置看能填什么这样以来,成功的样例直接TLE;

        然后我更换了我的策略:dfs存储当前所在位置,然后依次从左上往右下角枚举,这样相比前一种方法有了效率质的飞跃(不会去看已经填了的空,并且保证是枚举完了一行才去枚举下一行,保证有序性),可以秒过样例了,但是OJ上还是全TLE

        然后进一步优化,不再是呆板的从左上到右下枚举,而是每次枚举一个空,这个空是当前情况能填入数最少的一个,我加入了MMin函数、并且预处理出每个状态里面有多少个1:例如nu[(100100001)_{2}]= 3

        还有一个剪枝:就是在求最小的可填数的位置时,如果出现一个空的可填数是0,表明当前状态最终是不能达到正解的,直接return(这个优化了很多)

inline pair<int,int> MMin()
{
    int minn=10000;
    pair<int,int> ret=make_pair(0,0);
    for(int i=1;i<=9;i++){
        for(int j=1;j<=9;j++){
            if(ch[i][j]!='.')continue;
            int now=he[i]&shu[j]&sqr[z[i][j]];
            if(now==0)return make_pair(-1,-1);
            if(minn>nu[now]){
                minn=nu[now];
                ret.first=i;
                ret.second=j;
            }
        }
    }
    return ret;
}

        值得一提的是方案三A了这道题,但是用第二种方案和第三种方案分别跑强度低的数据和强度高的数据,对于强度低的,第三种比第二种快,而强度高的,第二种比第三种快(有之前TitanV和RTX2080那味了

#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
 
using namespace std;
 
char ch[20][20];
int he[10],shu[10],sqr[10];
int nu[2048];
int sum;
bool flag;
 
inline int lowbit(int x) {
    return x&(-x);
}
 
int z[10][10]={{0,0,0,0,0,0,0,0,0,0},
               {0,1,1,1,2,2,2,3,3,3},
               {0,1,1,1,2,2,2,3,3,3},
               {0,1,1,1,2,2,2,3,3,3},
               {0,4,4,4,5,5,5,6,6,6},
               {0,4,4,4,5,5,5,6,6,6},
               {0,4,4,4,5,5,5,6,6,6},
               {0,7,7,7,8,8,8,9,9,9},
               {0,7,7,7,8,8,8,9,9,9},
               {0,7,7,7,8,8,8,9,9,9}};
 
inline pair<int,int> MMin()
{
    int minn=10000;
    pair<int,int> ret=make_pair(0,0);
    for(int i=1;i<=9;i++){
        for(int j=1;j<=9;j++){
            if(ch[i][j]!='.')continue;
            int now=he[i]&shu[j]&sqr[z[i][j]];
            if(now==0)return make_pair(-1,-1);
            if(minn>nu[now]){
                minn=nu[now];
                ret.first=i;
                ret.second=j;
            }
        }
    }
    return ret;
}
 
inline bool dfs(int remain,int xx,int yy) {
    if(xx==-1)return 0;
        int now=he[xx]&shu[yy]&sqr[z[xx][yy]];
        if(now==0)return 0;
        for(int k=now; k; k-=lowbit(k)) {
            int num=lowbit(k);
            num=log(num)/log(2);
            ch[xx][yy]=(char)(num+'0'+1);
            he[xx]-=(1<<(num));
            shu[yy]-=(1<<(num));
            sqr[z[xx][yy]]-=(1<<(num));
            pair<int,int>d=MMin();
            if(d.first==-1){
                ch[xx][yy]='.';
                he[xx]+=(1<<(num));
                shu[yy]+=(1<<(num));
                sqr[z[xx][yy]]+=(1<<(num));
                continue;
            }
            if(d.first==0) return true;
            if(dfs(remain-1,d.first,d.second)) return true;
            ch[xx][yy]='.';
            he[xx]+=(1<<(num));
            shu[yy]+=(1<<(num));
            sqr[z[xx][yy]]+=(1<<(num));
        }
    return 0;
}
 
inline void prework()
{
    memset(nu,0,sizeof(nu));
    for(int i=1;i<=511;i++){
        int now=i;
        while(now){
            nu[i]++;
            now-=lowbit(now);
        }
}
}
 
int main(void) {
    prework();
    while(1) {
        memset(he,0,sizeof(he));
        memset(shu,0,sizeof(shu));
        memset(sqr,0,sizeof(sqr));
        for(int i=1; i<=9; i++) {
            for(int j=1; j<=9; j++) {
                cin>>ch[i][j];
                if(ch[i][j]=='e') {
                    return 0;
                }
            }//scanf("%c",ch[i][j]);
        }
        sum=0;
        flag=0;
        for(int i=1; i<=9; i++) {
            for(int j=1; j<=9; j++) {
                if(ch[i][j]!='.') {
                    he[i]|=1<<(ch[i][j]-'0'-1);
                    shu[j]|=1<<(ch[i][j]-'0'-1);
                    sqr[z[i][j]]|=1<<(ch[i][j]-'0'-1);
                } else sum++;
            }
        }
        for(int i=1; i<=9; i++) {
            he[i]=he[i]^511;
            shu[i]=shu[i]^511;
            sqr[i]=sqr[i]^511;
        }
        dfs(sum,MMin().first,MMin().second);
        for(int y=1; y<=9; y++)for(int t=1; t<=9; t++)putchar(ch[y][t]);
        putchar('\n');
    }
}

----------------------------------我是分隔线-------------------------------------

Day2T1:

Day2T1http://118.31.67.228/problem.php?cid=1024&pid=0

样例输入:

3 3
1 3 1 2
1 2 1 2
2 3 1 2

样例输出 :

2

题解: 

        这道题刚刚拿到的时候,有点不知所措,但是仔细分析,我们不能把A、B的行动放在一起看我们要将其分成两个部分,单独处理出A、B可能到达终点的时间。判断有没有相等的情况就可以了。

        注意:判断有没有相同的值时要用map,不然TLE警告

Day2T2:

Day2T2http://118.31.67.228/problem.php?cid=1024&pid=1

样例输入:

3 4
OXXO
XXOO
XOOO
3 2 2 4
3 3 1 1
0 0 0 0

样例输出 :

1
Impossible!

题解: 

        第一次看到这道题想的是如何快速的处理出地图上能攻击到敌人的位置,于是就被这里卡住了,考下来才听说,直接暴力处理出能a到的地方就可以了(当场emo,所以啊先把暴力写出来再去想优化吧

除了预处理写起来有点烦人以外,其他都还好

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>

using namespace std;

int n,m;
int ans;
int ko[300][300];
char ma[300][300];
int col[300][300];
int mov[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int x1,yy1,x2,y2;
bool flag;
int atck[300][300];
int dis[300][300];

inline bool kd(int x,int y)
{
	return (x>=1 && x<=n && y>=1 && y<=m);
}


queue< pair<int,int> >q;

inline void BFS()
{
	memset(dis,0,sizeof(dis));
	
	q.push(make_pair(x2,y2));
	
	dis[x2][y2]=0;
	while(!q.empty()){
		pair<int,int>now=q.front();q.pop();
		if(atck[now.first][now.second]==1){
			ans=dis[now.first][now.second];
			return ;
		}
		int x=now.first,y=now.second;
		for(int i=0;i<4;i++){
			if(	kd(x+mov[i][0],y+mov[i][1]) && ma[x+mov[i][0]][y+mov[i][1]]=='O' &&
				dis[x+mov[i][0]][y+mov[i][1]]==0){
				dis[x+mov[i][0]][y+mov[i][1]]=dis[x][y]+1;
				q.push(make_pair(x+mov[i][0],y+mov[i][1]));
			}
	}
	}
}

inline void dfs_2(int x,int y)
{
	col[x][y]=1;
	for(int i=0;i<4;i++){
		if(kd(x+mov[i][0],y+mov[i][1]) && ma[x+mov[i][0]][y+mov[i][1]]=='O' && col[x+mov[i][0]][y+mov[i][1]]==0){
			col[x+mov[i][0]][y+mov[i][1]]=1;
			dfs_2(x+mov[i][0],y+mov[i][1]);
		}
	}
}

inline void prework(int x,int y)
{
	memset(atck,0,sizeof(atck));
	
	for(int i=x;i<=n;i++){
		if(ma[i][y]=='X')break;
		atck[i][y]=1;
	}
	for(int i=x;i>=1;i--){
		if(ma[i][y]=='X')break;
		atck[i][y]=1;
	}
	for(int i=y;i<=m;i++){
		if(ma[x][i]=='X')break;
		atck[x][i]=1;
	}
	for(int i=y;i>=1;i--){
		if(ma[x][i]=='X')break;
		atck[x][i]=1;
	}
	for(int i=1;i<=min(n-x,m-y);i++){
		if(ma[x+i][y+i]=='X')break;
		atck[x+i][y+i]=1;
	}
	for(int i=1;i<=min(n-x,y);i++){
		if(ma[x+i][y-i]=='X')break;
		atck[x+i][y-i]=1;
	}
	for(int i=1;i<=min(x,m-y);i++){
		if(ma[x-i][y+i]=='X')break;
		atck[x-i][y+i]=1;
	}
	for(int i=1;i<=min(x,y);i++){
		if(ma[x-i][y-i]=='X')break;
		atck[x-i][y-i]=1;
	}
}

inline void clr()
{
	queue<pair<int ,int> >emp;
	swap(emp,q);
}

int main(void)
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>ma[i][j];
		}
	}
	scanf("%d%d%d%d",&x1,&yy1,&x2,&y2);
	while(x1+x2+yy1+y2){
		ans=-1;
		clr();
		prework(x1,yy1);
		BFS();
		if(ans==-1)printf("Impossible!\n");
		else printf("%d\n",ans);
		scanf("%d%d%d%d",&x1,&yy1,&x2,&y2);
	}
}
/*
3 4
OXXO
XXOO
XOOO
3 2 2 4
3 3 1 1
0 0 0 0
*/

Day2T3:

Day2T3http://118.31.67.228/problem.php?cid=1024&pid=2

样例输入:

4
aacc

样例输出 :

aacc
acac
acca
caac
caca
ccaa
6

题解:

        今天学到了一个高级东西,next_permutation(a+1,a+n+1,tmp),它可以得到任意长度的全排列,并且不会有重复,当所有排列都输出了它会自己回到原来的序列,所以当得到的序列再一次等于自身时,就结束。

#include<cstdio>
#include<iostream>
#include<map>
#include<cstring>
#include<string>
#include<algorithm>

using namespace std;

int n;
char ch[1000];
map<string , bool>Hash;
bool vis[1000];
int cnt;

int main(void)
{
	scanf("%d",&n);
	//cin.tie(0);cout.tie(0);
	cin>>ch;
	sort(ch,ch+n);
	while(1){
		Hash[ch]=true;
		cnt++;
		for(int i=0;i<n;i++)printf("%c",ch[i]);
		printf("\n");
		next_permutation(ch,ch+n);
		if(Hash[ch]!=0)break;
	}
	printf("%d",cnt);
}

Day2T4:

Day2T4http://118.31.67.228/problem.php?cid=1024&pid=3

样例输入:

4 5
.....
.X...
...*X
X.X..
3
NORTH
WEST
SOUTH

样例输出 :

.....
*X*..
*.*.X
X.X..

题解:

        这道题不用dfs,对于每一个操作,我们只关注每一个操作,把当前操作的所有状态都画在图上,下一个操作对这些操作再次拓展,这样历来更加高效稳定,注意一次操作不要把新鲜更新出来的状态也操作了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>

using namespace std;

int n,m;
int k;
char ma[300][300];
char s[200];
int st_x,st_y;

int main(void)
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>ma[i][j];
			if(ma[i][j]=='*')st_x=i,st_y=j;
		}
	}
	scanf("%d",&k);
	while(k--){
		cin>>s;
		if(s[0]=='N'){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					if(ma[i][j]=='*'){
						ma[i][j]='.';
						for(int l=i-1;l>=1;l--){
							if(ma[l][j]=='X')break;
							ma[l][j]='#';
						}
					}
				}
			}
		}
		if(s[0]=='S'){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					if(ma[i][j]=='*'){
						ma[i][j]='.';
						for(int l=i+1;l<=n;l++){
							if(ma[l][j]=='X')break;
							ma[l][j]='#';
						}
					}
				}
			}
		}
		if(s[0]=='W'){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					if(ma[i][j]=='*'){
						ma[i][j]='.';
						for(int l=j-1;l>=1;l--){
							if(ma[i][l]=='X')break;
							ma[i][l]='#';
						}
					}
				}
			}
		}
		if(s[0]=='E'){
			for(int i=1;i<=n;i++){
				for(int j=1;j<=m;j++){
					if(ma[i][j]=='*'){
						ma[i][j]='.';
						for(int l=j+1;l<=m;l++){
							if(ma[i][l]=='X')break;
							ma[i][l]='#';
						}
					}
				}
			}
		}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)if(ma[i][j]=='#')ma[i][j]='*';
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			printf("%c",ma[i][j]);
		}
		printf("\n");
	}
}
/*
4 5
.....
.X...
...*X
X.X..
3
NORTH
WEST
SOUTH

*/

总结:

        终于搜索完了,希望多复习,多温故,多有新的理解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值