3.8-3.14周总结

一、本周学习ACM相关内容:

   1.学习bfs、dfs六小时。(学习素材来源:挑战程序设计竞赛第二版)

二、题数与耗时

    1.做题vj平台最基础穷竭搜索11题

      耗时平均每题三小时左右

      补题及感想

      POJ-2386

      题意:一个‘W’为一个水洼,水洼及其八方的水洼组成一个大水洼,问大水洼有多少

      思路:遇到水洼‘W',直接进行t变量数字的标记,一个大水洼标记完之后t++,方便下次进行遍历时进行标记。通过染色进行标记,移动一般用dx,dy两个数组,或者一个二维数组进行标记,此题是八个方向,可用for循环直接进行遍历。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 //染色法,移动可用数组(-1,0)类似,
 6 //也可用for循环,递归考虑仔细,他的变换方式
 7 int m,n;
 8 int k,p,t=1,s=0;
 9 char a[101][101];
10 void zhuanhua(int c,int d);
11 void endd();
12 int main(){
13 
14     scanf("%d %d",&n,&m);
15     getchar();
16 
17     for(k=0;k<n;k++){
18         for(p=0;p<m;p++){
19             scanf("%c",&a[k][p]);
20         }
21         getchar();
22 
23     }
24     endd();
25 }
26 void endd(){
27     int k,p,t=0;
28     for(k=0;k<n;k++){
29         for(p=0;p<m;p++){
30             if(a[k][p]=='W'){
31                 zhuanhua(k,p);
32                 t++;
33             }
34         }
35     }
36     printf("%d",t);
37 }
38 void zhuanhua(int c,int d){
39     if(a[c][d]=='W'){
40         a[c][d]=t;
41     }
42     for(int i=-1;i<2;i++){
43         for(int j=-1;j<2;j++){
44             if(a[c+i][d+j]=='W'){
45                 zhuanhua(c+i,d+j);
46             }
47         }
48     }
49 }

      POT-1979

      题意:一个‘@’为起点,可以上下左右动,遇到‘.’即可走,遇到‘#’不可走,问可以遍历多少个‘.’

      思路:步骤大致同上题相同,先进行判断是否是‘.’,进行赋值为1的标记,然后进行for循环对其周围字符进行判断。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<vector>
 4 #include<set>
 5 #include<iostream>
 6 //步骤大致同1
 7 using namespace std;
 8 int m,n,k,t,f,s,con=0;
 9 char c[100][100];
10 int r[5][2]={{-1,0},{1,0},{0,-1},{0,1}};
11 void change(int f,int s);
12 bool panduan(int a,int b);
13 int main(){
14     while(~scanf("%d %d",&m,&n)){
15         if(m==0&&n==0){
16             break;
17         }
18         con=0;
19         getchar();
20         for(k=0;k<n;k++){
21             for(t=0;t<m;t++){
22                 scanf("%c",&c[k][t]);
23                 if(c[k][t]=='@'){
24                     f=k;
25                     s=t;
26                 }
27             }
28             getchar();
29         }
30         change(f,s);
31         for(k=0;k<n;k++){
32             for(t=0;t<m;t++){
33                 if(c[k][t]==1){
34                     con++;
35                 }
36             }
37         }
38         printf("%d\n",con+1);
39     }
40 }
41 void change(int f,int s){
42     int a,b;
43     if(c[f][s]=='.'){
44         c[f][s]=1;
45     }
46     for(a=0;a<4;a++){
47         if(f+r[a][0]<0||f+r[a][0]>=n||s+r[a][1]<=-1||s+r[a][1]>=m)continue;
48         if(c[f+r[a][0]][s+r[a][1]]=='.'){
49             change(f+r[a][0],s+r[a][1]);
50 
51         }
52 
53 
54 
55 
56 
57     }
58 
59 }

      Aizu-0118

      题意:‘@’、‘#’、‘*’及其附近相同的字符可以组成一个区域,问这些区域有多少

      !! 改了最多回的题,一定注意全局变量和局部变量的问题,很会影响输出结果。

      思路:从头开始遍历,先进行附近位置是否有相同字符的查找,如果有则进行赋值1,赋值结束并且附近无相同字符后,用一个t进行情况统计。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 using namespace std;//定义变量时,尽量随用随写,
 6 //不然全局变量和局部变量的变化让你找不到bug
 7 int h,w;
 8 char a[100][100];
 9 int r[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
10 void change(int c,int d,char e);
11 int main(){
12 
13     while(~scanf("%d %d",&h,&w)){
14     int t=0,m,n;
15     if(h==0&&w==0){
16         break;
17     }
18     getchar();
19     for(m=0;m<h;m++){
20         for(n=0;n<w;n++){
21             scanf("%c",&a[m][n]);
22         }
23         getchar();
24     }
25     for(m=0;m<h;m++){
26         for(n=0;n<w;n++){
27             if(a[m][n]=='#'||a[m][n]=='*'||a[m][n]=='@'){
28                 change(m,n,a[m][n]);
29                 t++;
30             }
31         }
32     }
33     printf("%d\n",t);
34     }
35 
36 }
37 void change(int c,int d,char e){
38     if(a[c][d]==e){
39         a[c][d]='1';
40     }
41     for(int s=0;s<4;s++){
42         if(c+r[s][0]<0||c+r[s][0]>=h||d+r[s][1]<=-1||d+r[s][1]>=w)continue;
43         if(a[c+r[s][0]][d+r[s][1]]==e){
44             change(c+r[s][0],d+r[s][1],e);
45 
46         }
47     }
48 }

      Aizu-0033

      题意:十个数有顺序输入,按题中所给固定顺序进行分组,分为两组,且两组数都是从小到大

      思路:此题可以先找此组数中最小的那个数,肯定是它做一边的底,然后进行最小数后面数的遍历,再找这些后面数的最小,依次循环

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 int a[10],b[10],c[10];
 8 void change(int i,int l){
 9     int mi,t,s=0,xu=0,sh;
10     mi=110000000;
11     for(t=l+1;t<10;t++){
12         if(a[t]>i&&a[t]<mi){
13             mi=a[t];
14             xu=t;
15             s=1;
16         }
17     }
18     a[0]=0;
19     a[xu]=0;
20     if(s==1){
21         change(mi,xu);
22     }
23 
24 }
25 
26 int main(){
27     int n,t,m,k,con=0;
28     scanf("%d",&t);
29     while(t--){
30         con=0;
31         for(m=0;m<10;m++){
32             scanf("%d",&a[m]);
33             b[m]=0;
34         }
35             k=0;
36             change(a[0],0);
37 
38             for(m=0;m<10;m++){
39                 if(a[m]==0) {continue;}
40                 else{
41                     b[k]=a[m];
42                     c[k]=a[m];
43                     k++;
44                 }
45             }
46             sort(b,b+k);
47 
48             for(m=0;m<k;m++){
49                 if(c[m]!=b[m]){
50                    con=1;
51 
52                 }
53             }
54 
55             if(con==1){
56                 printf("NO\n");
57             }else{
58                 printf("YES\n");
59             }
60 
61     }
62 }

      Aizu-0558

      题意:‘S’为出发点,进行寻找奶酪,奶酪从1-n,并且不可吃比其大的奶酪。

      思路:用数组d进行判定是否走过,因为不可吃比起大的奶酪,只需要让小老鼠从1-n的顺序吃奶酪即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<queue>
 4 #include<algorithm>
 5 #include<cstring>
 6 using namespace std;
 7 typedef pair<int,int> P;
 8 const int inf=0;
 9 int b[10000];
10 char a[1000][1000],ts[1000][1000];
11 int d[1000][1000];
12 int sx,sy;
13 int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1};
14 int h,w,n;
15 int coun(int sx,int sy,int gx,int gy);
16 int main(){
17 
18     int sum=0;
19     scanf("%d %d %d",&h,&w,&n);
20     getchar();
21     int k=1,sx=0,sy=0,gx=0,gy=0;
22 
23     for(int i = 0; i < h ;i++){
24         for(int j = 0; j < w ;j++){
25 
26             scanf("%c",&a[i][j]);
27             ts[i][j]=a[i][j];
28             if(a[i][j]=='S'){
29 
30                 sx=i;
31                 sy=j;
32             }
33 
34         }
35         getchar();
36     }
37     while(k<=n){
38 
39     for(int i = 0; i < h ;i++){
40         for(int j = 0; j < w ;j++){
41             if(ts[i][j]-'0'==k){
42                 gx=i;
43                 gy=j;
44                 a[i][j]='.';
45 
46             }
47         }
48     }
49     sum+=coun(sx,sy,gx,gy);
50     sx=gx;sy=gy;
51     k++;
52     }
53     printf("%d\n",sum-n);
54 
55 
56 }
57 int coun(int sx,int sy,int gx,int gy){
58     queue<P> que;
59     for(int i=0;i<h;i++){
60         for(int j=0;j<w;j++){
61             d[i][j]=inf;
62         }
63     }
64     que.push(P(sx,sy));
65     d[sx][sy]=1;
66     while(que.size()){
67         P p = que.front();que.pop();
68         if(p.first==gx&&p.second==gy)break;
69         for(int i=0;i<4;i++){
70             int nx=p.first + dx[i],ny = p.second + dy[i];
71             if(0<=nx&&nx<h&&0<=ny&&ny<w&&a[nx][ny]!='X'&&d[nx][ny]==inf){
72                 d[nx][ny]=1;
73                 que.push(P(nx,ny));
74 
75                 d[nx][ny]=d[p.first][p.second]+1;
76                }
77         }
78 
79     }
80 
81 
82     return d[gx][gy];
83 }

 

      POJ-3669

      题意:贝茜听说一场非凡的流星雨即将来临;有报道称,这些流星会撞向地球,并摧毁它们撞到的任何东西。由于担心自己的安全,她发誓要找到一个安全的地方(一个不会被流星摧毁的地方)。她目前正在坐标平面上的原点上吃草,想要移动到一个新的、更安全的地方,同时避免被沿途的流星摧毁。有报道称M颗流星(1≤M≤50,000颗)将会撞击,其中流星i将会撞击点(Xi, Yi)(0≤Xi≤300;0≤Yi≤300)时Ti(0≤Ti≤1000)。每颗流星都会摧毁它所撞击的点,以及四个直线相邻的晶格点。贝西时间0离开 ,并在第一象限以每秒钟1单位的速度与坐标轴平行移动到(通常是4个)尚未被流星摧毁的相邻直线点。她不能在任何大于或等于它被摧毁的时间点上被找到)。确定贝西到达安全地点的最短时间。

   !!从MLE -》TLE,真心提醒建议手写数组,尽量开的大一些,用了queue有时会TLE,而且这里的四个直线相邻的晶格点即流行点上下左右相邻的四个点

      思路:进行流行点上下左右点的标记,如果流行点有重合,需要进行min的计算取值,只需要对这个流行点进行抽象,在这个表格中进行最大值的赋值,或者初始化为负数,遍历直到走到的位置数值为初始化的数值。

 1 #include<iostream>
 2 #include<queue>
 3 #include<cstring>
 4 #include<queue>
 5 using namespace std;
 6 int inf=1000000;
 7 int dp[400][400]={},sum[400][400]={},fx[1000000]={},fy[1000000]={};
 8 int ch[4000][4000];
 9 int x,y,z,i,j;
10 int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
11 
12 int main(){
13     int sx=0,sy=0,m;
14     scanf("%d",&m);
15     for(int i=0;i<400;i++){
16         for(int j=0;j<400;j++){
17             ch[i][j]=inf;
18         }
19     }
20     for(int i=0;i<m;i++){
21             scanf("%d %d %d",&x,&y,&z);
22             ch[x][y]=min(z,ch[x][y]);
23             for(int j=0;j<4;j++){
24             int nx=x+dx[j],ny=y+dy[j];
25             if(nx>=0&&ny>=0){
26                 ch[nx][ny]=min(ch[nx][ny],z);
27             }
28         }
29     }//进行数表坐标最小标记
30 
31     int tail=1;
32     sum[0][0]=0;//sum时间
33     fx[1]=0;//分别表示x轴和y轴移动
34     fy[1]=0;
35     for(i=1;i<=tail;i++){
36         if(ch[fx[i]][fy[i]]>999999){//如果到了安全的地方
37             cout<<sum[fx[i]][fy[i]];
38             return 0;
39         }
40         for(j=0;j<=3;j++){
41             int xx=fx[i]+dx[j];
42             int yy=fy[i]+dy[j];
43             if(xx>=0&&yy>=0&&dp[xx][yy]==0&&sum[fx[i]][fy[i]]+1<ch[xx][yy]){
44                 dp[xx][yy]=1;
45                 sum[xx][yy]=sum[fx[i]][fy[i]]+1;
46                 fx[++tail]=xx;
47                 fy[tail]=yy;
48             }
49         }
50     }
51     cout<<-1;
52     return 0;
53 }

      Aizu-0121

      题意:7智力游戏由8个正方形的卡和这些卡紧紧相扣的框构成。每个卡片上都有0、1、2、…,号码是7。框可以竖着摆2个,横着摆4个卡片。开始解谜的时候,首先将所有的卡放入框中。在框中只有0的卡,可以和上下左右相邻的卡交换位置。直到变换为01234567

      思路:类似于进行打表,从01234567进行变换存入数组,然后通过输入的已知,进行查找其位置即可

 1 #include<iostream>
 2 #include<map>
 3 #include<string>
 4 #include<algorithm>
 5 #include<queue>
 6 using namespace std;
 7 map<string, int>a;
 8 //基本思路 类似于打表,因为结果已知
 9 int d[4] = { 1,-1,4,-4 };
10 void bfs() {
11     queue<string>que;
12     que.push("01234567");
13     a["01234567"] = 0;
14     while (!que.empty()) {
15         string s = que.front();que.pop();
16         int index;
17         for (int i = 0;i < 8;i++) {
18             if (s[i] == '0'){
19                 index = i;
20                 break;
21             }
22         }
23 
24         for (int i = 0;i < 4;i++) {
25             int x = index + d[i];
26             if (x >= 0 && x <= 7 && !(index == 3 && i == 0) && !(index == 4 && i == 1)) {
27                 string next=s;////右上角不能向右移、左下角不能向左移
28                 int temp=next[x];
29                     next[x]=next[index];
30                     next[index]=temp;
31                 if (a.find(next) == a.end()) {//说明容器里没有该元素,确认没有再进栈
32                     a[next]=a[s]+1;
33                     que.push(next);
34                 }
35             }
36         }
37 
38     }
39 }
40 int main() {
41     bfs();
42     string s;
43     while (getline(cin,s)) {
44     s.erase(remove(s.begin(), s.end(), ' '), s.end());//remove的时候只是通过迭代器的指针向前移动来删除,
45     //将没有被删除的元素放在链表的前面,并返回一个指向新的超尾值的迭代器
46         cout << a[s] << endl;
47     }
48     return 0;
49 }

      POJ-2718

      题意:给定一组不同的十进制数字,您可以通过选择这些数字的一个非空子集并按一定的顺序编写它们来形成一个整数。其余的数字可以按某种顺序写下来,以形成第二个整数。除非得到的整数是0,否则整数不能以数字0开始。例如,如果给定数字0、1、2、4、6和7,可以写出整数对10和2467。当然,有许多方法可以形成这样的整数对:210和764、204和176,等等。最后一对中整数的差值的绝对值是28,根据上面的规则所形成的任何一对之间的差值都是最小的。

      思路:用next_permutation进行全排列查找,依次遍历找最小

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<iostream>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 int a[10],num1,num2;
 8 char str[30];
 9 int main(){
10     int n,k,con,s;
11     scanf("%d",&n);
12     getchar();
13     while(n--){
14         k=0;
15         num1=0;num2=0;
16         gets(str);
17         int len=strlen(str);
18         for(int j=0;j<len;j++){
19             if(str[j]>='0'&&str[j]<='9'){
20                 a[k++]=str[j]-'0';
21             }
22         }
23         if(k==2){
24             printf("%d\n",a[1]-a[0]);
25             continue;
26         }else{
27             int i=0,t=k/2,m=k,s=k/2;
28             while(a[0]==0)
29             next_permutation(a,a+k);//进行全排列
30         int ans=999999999;
31         do
32         {//暴力全排列 进行数字比较差值
33             int mid=(k+1)/2;
34             if(a[mid])//第二个数不能以零开头
35             {
36                 int num1=0,num2=0;
37                 for(i=0;i<mid;++i)
38                     num1=num1*10+a[i];
39                 for(i=mid;i<k;++i)
40                     num2=num2*10+a[i];
41                 ans=min(ans,abs(num1-num2));
42             }
43         }while(next_permutation(a,a+k));
44         printf("%d\n",ans);
45     }
46     }
47 }

      POJ-3187

      题意:FJ和他的牛喜欢玩心理游戏。他们按一定的顺序写下从1到N的数字(1 <= N <= 10),然后将相邻的数字相加,得到一个少一个数字的新列表。他们重复这个过程,直到只剩下一个数字。

      思路:如上题差不多,巧用next_permutation,进行排列组合计算,直到找到

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 int a[20],v[20];
 7 int num(int *a,int n){
 8     int i,j,t=n;
 9     for(i=1;i<n;i++){
10         for(j=0;j<t;j++){
11             a[j]=a[j]+a[j+1];
12         }
13         t--;
14     }
15     return a[0];
16 }int main(){
17     memset(v,0,sizeof(v));
18     int i,n,sum;
19     scanf("%d %d",&n,&sum);
20     for(i=0;i<n;i++){
21         v[i]=i+1;
22     }
23     while(1){
24         for(int i=0;i<n;i++){
25             a[i]=v[i];
26         }//用另一股数组 进行计算  自身数组记录状态
27         if(num(a,n)==sum) break;
28         next_permutation(v,v+n);
29     }
30     printf("%d",v[0]);
31     for(int i=1;i<n;i++){
32             printf(" %d",v[i]);
33     }
34    printf("\n");
35     return 0;
36 }

      POJ-3050

      题意:奶牛用一种非传统的方式玩孩子的跳苏格兰威士忌游戏。奶牛们创建了一个5x5的平行于x和y轴的数字直线网格,而不是一组要跳进其中的线性数字框。然后,他们熟练地跳到网格中的任何数字上,并向前、向后、向右或向左(从不对角)跳到网格中的另一个数字。它们再次跳到一个数字(可能是已经访问过的数字)。总共有五个网格内跳转,它们的跳转创建一个六位整数(可能有前导零,如000201)。确定以这种方式创建的不同整数的数量。

      思路直接遍历 ,满足条件进行标记即可,利用set数组

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<set>
 6 #include<cstring>
 7 using namespace std;
 8 long long a[5][5];
 9 set<int> s;
10 int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},st[6]={};
11 int dfs(int x,int y,int z);
12 int main(){
13     int m,n;
14     for(m=0;m<5;m++){
15         for(n=0;n<5;n++){
16             scanf("%d",&a[m][n]);
17         }
18     }
19     for(m=0;m<5;m++){//想要换开头就得这么循环
20         for(n=0;n<5;n++){
21             st[0]=a[m][n];
22             dfs(m,n,1);//巧妙计数
23         }
24     }
25     printf("%d\n",s.size());
26 }
27 int dfs(int x,int y,int z){
28     int sum,k;
29     if(z==6){
30         sum=st[0]*100000+st[1]*10000+st[2]*1000+st[3]*100+st[4]*10+st[5];
31         s.insert(sum);
32         return 0;
33     }
34     for(k=0;k<4;k++){
35         int nx=x+dx[k],ny=y+dy[k];
36         if(nx>=0&&ny>=0&&nx<5&&ny<5){//找到第二个数了 告诉他自己是第二个数
37             st[z]=a[nx][ny];//先进数组再进行统一计算
38             dfs(nx,ny,z+1);
39         }
40 
41     }
42 }

      Aizu-0525

      题意:可翻转按行按列,⭕变为●,●变为⭕,单个●不可变换,问最多有多少个⭕

      思路:不管怎么变换,行列里面只要是●多就进行翻转即可,一行边进行翻转,边进行记录找到最大状

 

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<vector>
 5 using namespace std;
 6 int a[100][10000];
 7 int cou(int line);
 8 int ans=0;
 9 int m,n;
10 int main(){
11 
12     while(~scanf("%d %d",&m,&n)){
13         if(m==0&&n==0) break;
14         for(int i=0;i<m;i++){
15             for(int j=0;j<n;j++){
16                 scanf("%d",&a[i][j]);
17             }
18         }
19         cou(0);
20         printf("%d\n",ans);
21 
22 }
23 }
24 int cou(int line){//递归变换查找,固定一个变量进行参考,让列数变换去查找
25     //递归从不变到变一列变两列到核实最后一列变换以后,进行统计,边变换,边记录统计
26 
27     if(line==m){
28         int sum=0;
29         for(int i=0;i<n;i++){
30             int l=0;
31             for(int j=0;j<m;j++){
32                 if(a[j][i]==0){
33                     l++;
34                 }
35             }
36             sum+=max(l,m-l);
37         }
38         return ans=max(ans,sum);
39 
40     }
41     cou(line + 1);
42     for(int i = 0; i < n; i++){
43         if(a[line][i]==1){
44             a[line][i]=0;
45         }else{
46             a[line][i]=1;
47         }
48     }
49     cou(line + 1);
50 }

三、比赛情况

      此周因为有事,比赛请假。

四、锻炼情况

      每天20个俯卧撑。

 

posted @ 2020-03-16 21:56  bonel  阅读(204)  评论(0)    收藏  举报