ACM寒假集训Day-1入门知识(I~M)

不想一篇blog太长了,所以再来一篇写day1。


I - 棋盘问题

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

Input

输入含有多组测试数据。
每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
当为-1 -1时表示输入结束。
随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

Output

对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

Sample Input

2 1
#.
.#
4 4
...#
..#.
.#..
#...
-1 -1

Sample Output

2
1

 

 这道题用的是深度优先搜索dfs,题目要求每行每列不能有重复的棋子,所以就bfs每一行(i),然后遍历该行的每一列,如果是‘#’且满足行列均不重复,则可以放在这,放下以后接着下一行dfs(i+1),如果该行没有可以放置棋子的位置,则return到上一行。边界就是手中的棋子刚好放完。

贴代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 char a[9][9];
 7 int vis[9];//判断这一列是否放了棋子。
 8 int n,k,sum,m;
 9 void dfs(int i)
10 {
11   if(m==k){sum++;return;}//棋子放完了,结果+1,return回去寻找新的方案。
12   if(i>n)return;//行数越界要返回
13   for(int j=1;j<=n;j++)//开始遍历当前行的每一列
14   {
15     if(a[i][j]=='#'&&vis[j]==0)//如果这个点是‘#’且该列没有棋子,则放下棋子
16     {
17       m++;//放下的棋子数+1
18       vis[j]=1;//这个点标记
19       dfs(i+1);//递归下一行
20       m--;//回来后棋子回到手上
21       vis[j]=0;//取消这一列的标记
22     }
23   }
24   dfs(i+1);//这一行没得放继续下一行
25 }
26 int main()
27 {
28   memset(vis,0,sizeof(vis));
29   while(cin>>n>>k&&(n!=-1&&k!=-1))
30   {
31     sum=m=0;
32     for(int i=1;i<=n;i++)
33       for(int j=1;j<=n;j++)cin>>a[i][j];
34     dfs(1);
35     cout<<sum<<endl;
36   }
37 }

J - Sudoku

Sudoku is a very simple task. A square table with 9 rows and 9 columns is divided to 9 smaller squares 3x3 as shown on the Figure. In some of the cells are written decimal digits from 1 to 9. The other cells are empty. The goal is to fill the empty cells with decimal digits from 1 to 9, one digit per cell, in such way that in each row, in each column and in each marked 3x3 subsquare, all the digits from 1 to 9 to appear. Write a program to solve a given Sudoku-task.

Input

The input data will start with the number of the test cases. For each test case, 9 lines follow, corresponding to the rows of the table. On each line a string of exactly 9 decimal digits is given, corresponding to the cells in this line. If a cell is empty it is represented by 0.

Output

For each test case your program should print the solution in the same format as the input data. The empty cells have to be filled according to the rules. If solutions is not unique, then the program may print any one of them.

Sample Input

1
103000509
002109400
000704000
300502006
060000050
700803004
000401000
009205800
804000107

Sample Output

143628579
572139468
986754231
391542786
468917352
725863914
237481695
619275843
854396127

这道题是一道数独题,接着上一题的思考,我马上就有思路了,dfs!就是把1~9这9个数放进格子嘛,如果满足行、列、小九宫格不重复
就可以放进去,所以我就开了三个数组,来分别判断行、列、小九宫格内已经存放的数字。还是dfs,不过这里是搜索每一个格子,遍历1~9
这9个数,看看哪个数可以放进去,放进去就接着放下一个空,否则return回去前面放过的空重新放。
具体细节请看代码:
 1 #include<iostream>
 2 #include<cstring>
 3 using namespace std;
 4 int t;
 5 char a[10][10];//用char来储存数独是为了方便输入
 6 int row[10][10],column[10][10];//判断行和列的已经放下的数字
 7 int sq[4][4][10];//判断小九宫格里面已经放下的数字
 8 int flag=0;//储存结果
 9 int csq(int i)//这个函数是放在sq里面做下标的,用来判断当前格子在哪个小九宫格
10 {
11   if(i<=3)return 1;
12   else if(i<=6)return 2;
13   else if(i<=9)return 3;
14   return 0;
15 }
16 bool check(int i,int j,int k)//判断是否可以放下这个数字
17 {
18     if(row[i][k]==0&&column[j][k]==0&&sq[csq(i)][csq(j)][k]==0)return true;
19     return false;
20 }
21 int dfs(int x,int y)
22 {
23   if(x>9||y>9)//先判断是否越界
24   {
25     flag=1;
26     return 0;
27   }
28   if(a[x][y]!='0')
29   {
30     if(y==9)dfs(x+1,1);//如果这个位置已经有了非零数字,就搜下一个
31     else dfs(x,y+1);
32   }
33   else
34   {
35     for(int k=1;k<=9;k++)//在9个数中找出可以放入的数
36     {
37       if(check(x,y,k))
38       {
39         a[x][y]=k+'0';//放入这个数
40         row[x][k]=1;column[y][k]=1;sq[csq(x)][csq(y)][k]=1;//标记
41         if(y==9)dfs(x+1,1);//继续搜索下一个
42         else dfs(x,y+1);
43         if(flag)return 0;//如果完成了数独,则结束
44         a[x][y]='0';//否则取消标记,继续搜这个位置的数
45         row[x][k]=0;column[y][k]=0;sq[csq(x)][csq(y)][k]=0;
46       }
47     }
48     return 0;//9个数都搜完了,没有合适的,就返回去修改前面错误的数
49   }
50 }
51 int main()
52 {
53   int n;
54   cin>>n;
55   for(int t=0;t<n;t++)
56   {
57     memset(row,0,sizeof(row));
58     memset(column,0,sizeof(column));
59     memset(sq,0,sizeof(sq));
60     for(int i=1;i<=9;i++)
61     {
62       for(int j=1;j<=9;j++)
63       {
64         cin>>a[i][j];
65         if(a[i][j]!='0')//对输入的数做好标记
66         {
67           row[i][a[i][j]-'0']=1;
68           column[j][a[i][j]-'0']=1;
69           sq[csq(i)][csq(j)][a[i][j]-'0']=1;
70         }
71       }
72     }
73     flag = 0;
74     dfs(1,1);
75     for(int i=1;i<=9;i++)
76     {
77         for(int j=1;j<=9;j++)cout<<a[i][j];
78         cout<<endl;
79     }
80   }
81   return 0;
82 }

 
  

 K - 迷宫问题

定义一个二维数组:
int maze[5][5] = {

0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

Input

一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。

Output

左上角到右下角的最短路径,格式如样例所示。

Sample Input

0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0

Sample Output

(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)

这道题就不能用bfs了,我一来就用dfs做,只能得到最少步数,无论怎样都得不到路径,经过漫长的思考 (百度),终于用广度优先搜索bfs解决了问题,bfs我是在《啊哈算法》里面彻底搞懂的。
广度优先搜索是一层一层搜索的,把每一点的下一步可以到达的点搜一遍,并且这样得到的路径是最短的,因为是一层一层搜索的嘛(其实我也说不清楚)。我们记录每一个点的“父点"就可以把路径打印出来了,于是又想到用结构体来记录一个到这个点的步数和这个点的”父点“。
具体请赏析代码:
 1 #include<iostream>
 2 using namespace std;
 3 
 4 struct node
 5 {
 6     int x,y;//这个点的坐标
 7     int fa;//父点
 8     int step;//到这个点的最短步数
 9 };
10 struct node q[26];
11 int vis[5][5]={0};
12 int head=1,tail=1,px=0,py=0;//head是队列的头部,tail是尾部,最先放入的在头部
13 int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//方向数组便于搜索
14 int maze[5][5];
15 void bfs()
16 {
17   q[head].x=0;q[head].y=0;q[head].step=0;q[head].fa=0;//从(0,0)开始搜索
18   tail++;
19   vis[px][py]=1;//记得标记第一个点
20   int result = 0;
21   while(head<tail)//head==tail的时候已经搜索了所有的点了
22   {
23     for(int i=0;i<4;i++)//尝试从这个点出发到四个方向的点
24     {
25       px=q[head].x+dir[i][0];
26       py=q[head].y+dir[i][1];
27       if(px<0||py<0||px>4||py>4)continue;//越界则换方向
28       if(vis[px][py]==0&&maze[px][py]==0)//往这个方向走的点,没有标记且不是”墙“
29       {
30         vis[px][py]=1;//标记这个点
31         q[tail].x=px;//压入队列
32         q[tail].y=py;
33         q[tail].fa=head;//记录这个点的父点
34         q[tail].step=q[head].step+1;//到这个点的步数是到父点的步数+1
35         tail++;//尾部加长,继续搜索
36       }
37       if(px==4&&py==4)//搜到了终点
38       {
39         result = 1;
40         break;
41       }
42     }
43     head++;//这个点能一步到达的所有点搜完以后搜下一个点
44     if(result)break;
45   }
46 }
47 
48 int main()
49 {
50   for(int i=0;i<5;i++)
51     for(int j=0;j<5;j++)cin>>maze[i][j];
52   bfs();
53   int c=tail-1,i=0;
54   struct node t[20];//反过来存路径,因为路径要正着打印
55   while(c!=0)
56   {
57     t[i].x=q[c].x;
58     t[i].y=q[c].y;
59     i++;
60     c=q[c].fa;
61   }
62   for(int j=i-1;j>=0;j--)cout<<"("<<t[j].x<<", "<<t[j].y<<")"<<endl;//这逗号后面有个空格可真的是考眼力啊
63 }

L - Catch That Cow

Farmer John has been informed of the location of a fugitive cow and wants to catch her immediately. He starts at a point N (0 ≤ N ≤ 100,000) on a number line and the cow is at a point K (0 ≤ K ≤ 100,000) on the same number line. Farmer John has two modes of transportation: walking and teleporting.

* Walking: FJ can move from any point X to the points X - 1 or X + 1 in a single minute
* Teleporting: FJ can move from any point X to the point 2 × X in a single minute.

If the cow, unaware of its pursuit, does not move at all, how long does it take for Farmer John to retrieve it?
Input
Line 1: Two space-separated integers: N and K
Output
Line 1: The least amount of time, in minutes, it takes for Farmer John to catch the fugitive cow.
Sample Input
5 17
Sample Output
4

这道题的题意就是从点x到点y,一步只能从x点到x+1,x-1或x*2,求x到y的最少步数。我一开始在想这是不是贪心啊,但找不到最优子解,就很难受,最后还是百度了。知道这个是bfs以后我瞬间醒悟,马上自己就敲出来了。 (做出来以后觉得这就是bfs啊太明显了把)
直接贴代码吧,稍微熟悉了bfs以后就很容易敲这个题的:
 1 #include<iostream>
 2 #include<cstring>
 3 #include<queue>
 4 using namespace std;
 5 struct node{//结构体记录点坐标和到这个点的最短步数
 6   int x;
 7   int step;
 8 }t,p,temp;
 9 queue<struct node>q;
10 int vis[100005];
11 int next(int n,int k)//这个函数是用来方便for一次性遍历下一步操作的
12 {
13   if(k==0)return n+1;
14   if(k==1)return n-1;
15   if(k==2)return n*2;
16   return 0;
17 }
18 bool check(struct node u)//判断这个点是否找过
19 {
20   if(u.x<0||u.x>100000||vis[u.x]==1)return false;
21   return true;
22 }
23 int bfs(int n,int m)
24 {
25   while(!q.empty())q.pop();//开始之前一定要把队列清空
26   t.x=n;t.step=0;
27   vis[n]=1;
28   q.push(t);//放入第一个点
29   if(n==m)return 0;//起点和终点一样就是0步咯
30   while(!q.empty())
31   {
32     p=q.front();
33     q.pop();
34     for(int k=0;k<3;k++)//搜索当前点一步能到的点
35     {
36       temp.x=next(p.x,k);
37       temp.step=p.step+1;//到子点的步数是父点为父点步数+1
38       if(temp.x==m)return temp.step;//到了终点直接输出结果结束。
39       if(check(temp))//否则如果这个点没访问过,就放入队列
40       {
41         q.push(temp);
42         vis[temp.x]=1;
43       }
44     }
45   }
46   return 0;
47 }
48 int main()
49 {
50   int n,m;
51   while(cin>>n>>m)
52   {
53     memset(vis,0,sizeof(vis));
54     cout<<bfs(n,m)<<endl;
55   }
56   return 0;
57

M- 三分(离散)

You are given a multiset S consisting of positive integers (initially empty). There are two kind of queries:

  1. Add a positive integer to S, the newly added integer is not less than any number in it.
  2. Find a subset s of the set S such that the value  is maximum possible. Here max(s) means maximum value of elements in s — the average value of numbers in s. Output this maximum possible value of .
Input

The first line contains a single integer Q (1 ≤ Q ≤ 5·105) — the number of queries.

Each of the next Q lines contains a description of query. For queries of type 1 two integers 1 and x are given, where x (1 ≤ x ≤ 109) is a number that you should add to S. It's guaranteed that x is not less than any number in S. For queries of type 2, a single integer 2 is given.

It's guaranteed that the first query has type 1, i. e. S is not empty when a query of type 2 comes.

Output

Output the answer for each query of the second type in the order these queries are given in input. Each number should be printed in separate line.

Your answer is considered correct, if each of your answers has absolute or relative error not greater than 10 - 6.

Formally, let your answer be a, and the jury's answer be b. Your answer is considered correct if .

Input
6
1 3
2
1 4
2
1 8
2
Output
0.0000000000
0.5000000000
3.0000000000
Input
4
1 1
1 4
1 5
2
Output
2.0000000000

标题都说了三分,可我还是一点思路都没有。后来百度,看了n久才看懂,害。
其实搞明白为什么使用三分这道题就可以秒杀了。
因为:这里要求的子序列s一定包含原序列的最大值和前n项,这个n到底是多少,就是我们要搜的,而且max-mean的值随n的增大先增后减。
这里两个问题:
1,为什么包含最大值?你随便选一个不含原序列最大值的子序列,然后把子序列的最大值换成原序列的最大值,可算得max-mean是增大的。
2,为什么一定包含子序列的最前面几项?现在随机选一个k个元素的子序列,把前k-1项换成原序列的1到k-1项,可算得max-mean是增大的。
于是就在搜索出让max-mean最大的值选的是前n项的这个n。 (这就变成了二分,怪事,可能我的想法比较天真,不过是a了的)
贴代码:
 1 #include<iostream>
 2 #include<cmath>
 3 #include<cstdio>
 4 using namespace std;
 5 const int  MAXN=5e5+5;
 6 #define eps 1e-6
 7 #define  ll long long
 8 ll a[MAXN]={0};
 9 ll Q,t,x,cnt;
10 double f(ll x)//f(x)就是选序列前x项和最大项得到的子序列的max-mean的值
11 {
12   double ave;
13   ave=double(a[x]+a[cnt]-a[cnt-1])/(x+1);
14   return double(a[cnt]-a[cnt-1])-ave;
15 }
16 double sf(int l,int r)
17 {
18   if(l>r)return 0;
19   while(l+1<r)
20   {
21     //cout<<"ok";
22     int mid=(r+l)/2;
23     if(f(mid)>f(mid-1))l=mid;//根据f(x)随x的增大先增后减
24     else r=mid;
25   }
26   return f(l);
27 }
28 int main()
29 {
30   cnt=0;
31   cin>>Q;
32   for(int i=0;i<Q;i++)
33   {
34     cin>>t;
35     if(t==1)
36     {
37       cin>>x;
38       cnt++;//cnt记录当前输入的数的个数
39       a[cnt]=a[cnt-1]+x;
40     }
41     if(t==2)
42     {
43       //cout<<a[cnt]<<" ";
44       double flag = sf(1,cnt);
45       printf("%.10lf\n",flag);//10个零,第一次数了9个零把我wa了/(ㄒoㄒ)/~~
46     }
47   }
48 }

小结:day1的题到这里就结束了,其实acm队的学长给的课件的知识点基本上都是考百度和《啊哈算法》搞懂的,不过搞懂了还是很高兴。做完day1的入门知识,希望我是真的入门了吧哈哈。
接下来的会越来越难,不过我相信我可以坚持下去的,come on!

猜你喜欢

转载自www.cnblogs.com/hialin545/p/12237227.html