2017寒假练习题解 第二周 1.23-1.29

1.23

Problem A Luxurious Houses

题意:给 n 个数 a[i],问使得 a[i] 为 [i,n] 最大值的时候需要给 a[i] 增加多少

简析:可以倒着扫一遍,维护一个 Max[i] 表示 从[i,n] 的最大值,如果 a[i] > Max[i+1] ,就是0,否则就是 Max[i+1]+1-a[i]

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 
 7 const int maxn = 100000 + 10;
 8 
 9 int a[maxn], maxh[maxn];
10 
11 int main()
12 {
13     int n; scanf("%d", &n);
14     for(int i = 1; i <= n; i++) scanf("%d", a + i);
15     for(int i = n; i > 0; i--) maxh[i] = max(a[i], maxh[i+1]);
16 
17     for(int i = 1; i < n; i++) {
18         if(a[i] > maxh[i+1]) printf("0 ");
19         else printf("%d ", maxh[i+1] + 1 - a[i]);
20     }
21     printf("0\n");
22 
23     return 0;
24 }
参考代码

 

还可以用一个更笨的办法,每次直接用线段树查询[i,n] 的最大值

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 
 8 #define getmid(l,r) ((l) + ((r) - (l)) / 2)
 9 
10 const int maxn = 200005;
11 int w[maxn],h[maxn],res[maxn];
12 int n;
13 int nmax;
14 
15 struct node{
16     int l,r,maxx;
17 }t[4*maxn];
18 
19 void Push_up(int p){
20     t[p].maxx = max(t[p<<1].maxx,t[p<<1|1].maxx);
21 }
22 
23 void Build_tree(int p,int l,int r){
24     t[p].l = l;
25     t[p].r = r;
26     if(l == r){
27         t[p].maxx = h[l];
28         return;
29     }
30     int mid = getmid(l,r);
31     Build_tree(p<<1,l,mid);
32     Build_tree(p<<1|1,mid+1,r);
33     Push_up(p);
34 }
35 
36 void update(int idx,int p,int w){
37     if(t[p].l == t[p].r){
38         t[p].maxx = w;
39         return;
40     }
41     int mid = getmid(t[p].l,t[p].r);
42     if(idx <= mid) update(idx,p<<1,w);
43     else update(idx,p<<1|1,w);
44     Push_up(p);    
45 }
46 
47 void Query(int p,int l,int r){
48     if(t[p].maxx <= nmax) return;
49     if(t[p].l == l && t[p].r == r){
50         nmax = max(nmax,t[p].maxx);
51         return;
52     }
53     int mid = getmid(t[p].l,t[p].r);
54     if(r <= mid) Query(p<<1,l,r);
55     else if(l > mid) Query(p<<1|1,l,r);
56     else{
57         Query(p<<1,l,mid);
58         Query(p<<1|1,mid+1,r);
59     }
60 }
61 
62 int main(){
63     while(scanf("%d",&n) != EOF){
64         memset(h,0,sizeof(h));
65         for(int i = 1;i <= n;i++) scanf("%d",&h[i]);
66         Build_tree(1,1,n);
67         
68     //    for(int i = 1;i <= 2*n;i++)
69     //    printf("t[%d].l = %d  t[%d].r = %d  t[%d].maxx = %d\n",i,t[i].l,i,t[i].r,i,t[i].maxx);
70         
71         for(int i = 1;i <= n;i++){
72             if(i == n) res[i] = 0;
73             else{
74                 nmax = -1;
75                 Query(1,i+1,n);
76                 int b = nmax;//printf("i = %d  b = %d\n",i,b);
77                // printf("i = %d  nmaxx = %d\n",i,nmax);
78                 if(b < h[i]) res[i] = 0;
79                 else res[i] = b+1-h[i];
80             }    
81         }
82         
83         for(int i = 1;i <= n;i++) printf("%d ",res[i]);
84         printf("\n");
85     }
86     return 0;
87 }
参考代码

 

Problem B Developing Skills

题意:给出 n 个数 a[i],k次操作,每次操作可以将a[i] 增加1,每个数的收益是floor(a[i]/10),问最大的收益

简析:可以贪心的想,离整十更近的数 需要的操作数更少,所以按照 a[i]%10排序后,扫一遍就可以了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 
 8 const int maxn = 1e5+5;
 9 int n,k;
10 
11 struct node{
12     int x,y;
13 }p[maxn];
14 
15 int cmp(node n1,node n2){
16     return n1.y < n2.y;
17 }
18 
19 int cmp0(node n1,node n2){
20     return n1.x < n2.x;
21 }
22 
23 int a[105];
24 
25 void solve(){
26     for(int i = 1;i <= n;i++){
27         int pos = lower_bound(a+1,a+10,p[i].x) - a;
28         if(a[pos] == p[i].x) pos++;
29         p[i].y = a[pos] - p[i].x;
30     }
31     sort(p+1,p+n+1,cmp);
32     int ans = 0;
33     for(int i = 1;i <= n;i++){
34         if(k >= p[i].y){
35             ans += (p[i].x + p[i].y)/10;
36             k -= p[i].y;
37             p[i].x = p[i].x + p[i].y;
38         }
39         else {
40             ans += p[i].x/10;
41         }
42     }
43     if(k){
44         for(int i = 1;i <= n;i++){
45             int l = 10 - p[i].x/10;
46             int r = k/10;
47             if(r >= l){
48                 ans += l;
49                 k = k-l*10;
50             }
51             else{
52                 ans += k/10;
53                 k = k%10;
54             }
55             if(k < 10) break;
56         }
57     }
58     printf("%d\n",ans);
59 }
60 
61 int main(){
62     for(int i = 1;i <= 10;i++) a[i] = i*10;
63     a[11] = 100;
64     // freopen("in.txt","r",stdin);
65     // freopen("out.txt","w",stdout);
66     while(scanf("%d %d",&n,&k) != EOF){
67         for(int i = 1;i <= n;i++){
68             scanf("%d",&p[i].x);
69         }
70         solve();
71     }
72     return 0;
73 }
参考代码

 

Problem C Three Logos

题意:给出 3 个矩形,问能否拼成一个正方形,如果能的话,输出正方形的边长和正方形

简析:暴力。可以先扫出最大的一条矩形边,这条边肯定作为正方形的边长,再枚举剩下的两个矩形的组合方式

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<vector>
  6 using namespace std;
  7 
  8 char g[105];
  9 char res[105][105];
 10 
 11 struct node{
 12     int x,y;
 13 }a[5];
 14 
 15 int x2,y2,x3,y3,x1,y1;
 16 int flag;
 17 
 18 void work(int x2,int y2,int x3,int y3,char c1,char c2){
 19     if((y3 == y2) && (y3 + x1) == y1 && (x2 + x3) == y1){
 20         flag = 1;
 21         for(int i = 1;i <= x2;i++){
 22             for(int j = x1+1;j <= y1;j++) res[i][j] = c1;
 23         }
 24         for(int i = x2+1;i <= y1;i++){
 25             for(int j = x1+1;j <= y1;j++) res[i][j] = c2;
 26         }
 27     }
 28     
 29     if((y2 == y3) && (y2 == y1) && (x1 + x2 + x3) == y1){
 30         flag = 1;
 31         for(int i = 1;i <= y1;i++){
 32             for(int j = x1 +1;j <= x1+x2;j++) res[i][j] = c1;
 33         }
 34         for(int i = 1;i <= y1;i++){
 35             for(int j = x1+x2+1;j <= y1;j++) res[i][j] = c2;
 36         }
 37     }
 38 }
 39 
 40 void print(){
 41     printf("%d\n",y1);
 42     for(int i = 1;i <= y1;i++){
 43         for(int j = 1;j <= y1;j++) printf("%c",res[i][j]);
 44         printf("\n");
 45     }
 46     printf("\n");
 47 }
 48 
 49 void solve(){
 50     g[1] = 'A'; g[2] = 'B'; g[3] = 'C';
 51     int maxx = -1;
 52     for(int i = 1;i <= 3;i++) {
 53         maxx = max(maxx,a[i].x);
 54         maxx = max(maxx,a[i].y);
 55     }
 56 
 57     int pos = 0;
 58     for(int i = 1;i <= 3;i++){
 59         if(a[i].x == maxx || a[i].y == maxx){
 60             pos = i;
 61             break;
 62         }
 63     }
 64     if(a[pos].x > a[pos].y) swap(a[pos].x,a[pos].y);
 65     
 66     x1 = a[pos].x; y1 = a[pos].y;
 67     
 68     for(int i = 1;i <= y1;i++){
 69         for(int j = 1;j <= x1;j++) res[i][j] = g[pos];
 70     }
 71     
 72     int ok = 0;
 73     char c1,c2;
 74     for(int i = 1;i <= 3;i++){
 75         if(i != pos && ok == 0) {
 76             x2 = a[i].x;
 77             y2 = a[i].y;
 78             c1 = g[i];
 79             ok = 1;
 80         } 
 81         if(i != pos && ok){
 82             x3 = a[i].x;
 83             y3 = a[i].y;
 84             c2 = g[i];
 85         }
 86     }
 87     flag = 0;
 88     work(x2,y2,x3,y3,c1,c2);
 89     if(flag) {
 90         print();
 91         return;
 92     }
 93     work(x2,y2,y3,x3,c1,c2);
 94     if(flag) {
 95         print();
 96         return;
 97     }
 98     work(y2,x2,x3,y3,c1,c2);
 99     if(flag) {
100         print();
101         return;
102     }
103     work(y2,x2,y3,x3,c1,c2);
104     if(flag) {
105         print();
106         return;
107     }
108     puts("-1");
109 }
110 
111 int main(){
112     while(scanf("%d %d %d %d %d %d",&a[1].x,&a[1].y,&a[2].x,&a[2].y,&a[3].x,&a[3].y) != EOF){
113         solve();
114     }
115     return 0;
116 }
参考代码

一神的简洁的代码

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int a[6][3] = { 0, 1, 2,  0, 2, 1,  1, 0, 2,  1, 2, 0,  2, 0, 1,  2, 1, 0 };
 5 int b[8][6] = {
 6     0, 1, 0, 1, 0, 1,  0, 1, 0, 1, 1, 0,  0, 1, 1, 0, 0, 1,  0, 1, 1, 0, 1, 0,
 7     1, 0, 0, 1, 0, 1,  1, 0, 0, 1, 1, 0,  1, 0, 1, 0, 0, 1,  1, 0, 1, 0, 1, 0,
 8 };
 9 char c[3] = {'A', 'B', 'C'};
10 inline void pl(int x, int y){for(int i = 0; i < x; i++) putchar(c[y]);}
11 int d[3][2];
12 
13 int main(void)
14 {
15     for(int i = 0; i < 3; i++) scanf("%d %d", &d[i][0], &d[i][1]);
16     for(int i = 0; i < 6; i++)
17     {
18         for(int j = 0; j < 8; j++)
19         {
20             int A = a[i][0], B = a[i][1], C = a[i][2];
21             int xa = d[A][b[j][0]], ya = d[A][b[j][1]];
22             int xb = d[B][b[j][2]], yb = d[B][b[j][3]];
23             int xc = d[C][b[j][4]], yc = d[C][b[j][5]];
24             if(ya == yb && xa + xb == xc && ya + yc == xc)
25             {
26                 printf("%d\n", xc);
27                 for(int p = 1; p <= ya; p++) pl(xa, A), pl(xb, B), puts("");
28                 for(int p = 1; p <= yc; p++) pl(xc, C), puts("");
29                 return 0;
30             }
31             if(ya == yb && yb == yc && xa + xb + xc == ya)
32             {
33                 printf("%d\n", ya);
34                 for(int p = 1; p <= ya; p++) pl(xa, A), pl(xb, B), pl(xc, C), puts("");
35                 return 0;
36             }
37         }
38     }
39     puts("-1");
40     return 0;
41 }
参考代码

搜索1 棋盘问题

题意:在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,任意的两个棋子不能同行或同列,求摆放k个棋子的所有可行的摆放方案数C。

简析:这题只需要深搜,每次从上一个放棋子地方的下一行开始寻找可以放棋子的地方,当发现该点时,记录行数,并更新棋盘,将于此点同行同列的都更新为'.',如果找不到,则返回,当把所有棋子都放上去的时候,则找到一个接,计数+1。

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
struct prog {
    char map [10][10];    //棋盘
    int x;                //上一个棋子的行数
};
int m;                    //棋盘大小
int ans;                //解的个数
void DFS(prog tmp,int n)
{
    if(n==0)
    {//如果要求放的所有棋子都已经放好了,则索命找到了答案,则计数+1并返回
        ans++;
        return ;
    }
    int i , j ;
    for ( i = tmp.x + 1 ; i <= m -n; i ++ )
    {//从上个棋子的下一行开始搜,如果剩余可放棋子数明显大于剩余行数,则可以知道此时无解
        for ( j = 0 ; j < m ; j ++ )
        {//从该行的第一个棋盘开始搜到最后一个
            if ( tmp.map[i][j]=='#')
            {//如果找到了一点可以放棋子
                prog tmp2;
                tmp2=tmp;
                tmp2.x=i;//记下改点的行数
                int k;
                for (k = i+1 ; k < m ; k ++)
                {//更新棋盘,因为,不会再向该行和该行之前的棋盘搜索,故只用更新该行下同列的棋盘
                    tmp2.map[k][j]='.';
                }
                DFS(tmp2,n-1);//放好一个棋子,继续搜
                
            }
        }
    }
}
int main()
{
    
    int  n ;            //要求放棋子的多少
    while ( cin >> m >> n , m != -1 || n != -1 )
    {
        ans=0;//初始化为0
        prog map;
        map.x=-1;//此时还未放棋子,初始化为-1
        int i;
        for ( i = 0 ; i < m ; i ++ )
        {
            cin >> map . map [ i ];
        }
        DFS(map,n);
        cout<<ans<<endl;
    }
    return 0;
}
参考代码

搜索2 连连看

题意:对于已有的连连看图像(由数字表示),判断其中的两个点能否对消(按照连连看的规则)。

简析:直接对路径进行搜索,最终判断能否走到终点即可。

dfs代码:一个剪枝:即当弯数为二时,若此时点的方向不能直接到达目标点就减去此点。

 1 #include <cstdio>  
 2 #include <cstdlib>  
 3 #include <cstring>  
 4 #include <cmath>  
 5 #include <queue>  
 6 #include <climits>  
 7   
 8 using namespace std;  
 9   
10 const int MAX = 1003;  
11 const int dirx[5] = {0,0,1,0,-1};  
12 const int diry[5] = {0,1,0,-1,0};  
13   
14 bool visit[MAX][MAX];  
15 int map[MAX][MAX];  
16 int wan[MAX][MAX];  
17 int n,m,bx,by;  
18 bool mark;  
19   
20 bool yes(int x,int y,int dir){  
21     int dx = bx - x;  
22     int dy = by - y;  
23     if(dx!=0)dx = dx/abs(dx);  
24     if(dy!=0)dy = dy/abs(dy);  
25     if(dx==dirx[dir] && dy==diry[dir])return true;  
26     else return false;  
27 }  
28   
29 void dfs(int x,int y,int cnt,int dir){  
30     int i,tx,ty;  
31     if(mark)return;  
32     if(x<1 || y<1 || x>n || y>m || cnt>2)return;  
33     //注意下面几个剪枝的顺序,顺序搞错了就会出错,因为最后一个元素非0  
34     if(x==bx && y==by){  
35         mark = true;  
36         return;  
37     }  
38     if(cnt==2 && !yes(x,y,dir))return;//这个剪枝不强力,加了此剪枝时间只减少了18ms  
39     if(map[x][y]!=0)return;  
40     if(wan[x][y]!=-1 && wan[x][y]<=cnt)return;  
41     wan[x][y] = cnt;  
42     for(i=1;i<=4;++i){  
43         tx = x + dirx[i];  
44         ty = y + diry[i];  
45         if(dir!=i){  
46             dfs(tx,ty,cnt+1,i);  
47         }else{  
48             dfs(tx,ty,cnt,i);  
49         }  
50     }  
51 }  
52   
53 int main(){  
54 //  freopen("in.txt","r",stdin);  
55     int i,j,t,ax,ay;  
56     while(scanf("%d %d",&n,&m)!=EOF){  
57         if(n==0 && m==0)break;  
58         for(i=1;i<=n;++i){  
59             for(j=1;j<=m;++j){  
60                 scanf("%d",&map[i][j]);  
61             }  
62         }  
63         scanf("%d",&t);  
64         while(t--){  
65             memset(wan,-1,sizeof(wan));  
66             scanf("%d %d %d %d",&ax,&ay,&bx,&by);  
67             mark = false;  
68             if(map[ax][ay]!=map[bx][by] || map[ax][ay]==0){  
69                 printf("NO\n");  
70                 continue;  
71             }  
72             wan[ax][ay] = 0;  
73             for(i=1;i<=4;++i){  
74                 dfs(ax+dirx[i],ay+diry[i],0,i);  
75             }  
76             if(mark){  
77                 printf("YES\n");  
78             }else{  
79                 printf("NO\n");  
80             }  
81         }  
82     }  
83       
84     return 0;  
85 }  
参考代码

bfs代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 #define N 1003
 6 int m,n,g[N][N],i,j,q,x1,x2,y1,y2,x,y,ans,dx[]={-1,0,1,0},dy[]={0,1,0,-1},vis[N][N];
 7 bool dfs(int x1,int y1,int x2,int y2,int x,int y){
 8     if(x1==x2&&y1==y2){
 9         return 1;
10     }
11     else {
12         for(int i=0;i<4;i++){
13             int nx=x1+dx[i];
14             int ny=y1+dy[i];
15 
16 
17 
18 //            if(nx==x2&&ny==y2) return 1;
19             if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&vis[nx][ny]==0&&(g[nx][ny]==0||(nx==x2&&ny==y2))){
20 //            printf("%d %d %d %d %d %d %d %d\n",x1,y1,nx,ny,vis[2][4],g[nx][ny],x,y);
21 
22 
23                 if(y!=-1&&y!=i){
24                     vis[nx][ny]=1;
25                 if(x+1>2) {
26 
27                         vis[nx][ny]=0;
28                             continue;
29                         }
30 
31                 if(dfs(nx,ny,x2,y2,x+1,i)==1) return 1;
32                 vis[nx][ny]=0;
33                 continue;
34                 }
35                 else{
36                     vis[nx][ny]=1;
37                 ans++;
38                 if(dfs(nx,ny,x2,y2,x,i)==1) return 1;
39                 vis[nx][ny]=0;
40                 continue;
41                 }
42 
43 
44             }
45 
46         }
47         return 0;
48     }
49 }
50 int main(){
51     while(scanf("%d %d",&n,&m),m||n){
52         for(i=1;i<=n;i++){
53             for(j=1;j<=m;j++){
54                     scanf("%d",&g[i][j]);
55             }
56         }
57         scanf("%d",&q);
58         while(q--){
59                 memset(vis,0,sizeof(vis));
60             scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
61             if(g[x1][y1]==g[x2][y2]&&g[x1][y1]!=0){
62                     vis[x1][y1]=1;
63          //           printf("1\n");
64           //          printf("%d\n",g[2][1]);
65                 if(dfs(x1,y1,x2,y2,0,-1)==1){
66                     printf("YES\n");
67                 }
68                 else {
69                     printf("NO\n");
70                 }
71             }
72             else{
73                 printf("NO\n");
74             }
75         }
76     }
77 }
参考代码

 

1.24

Problem A Robot's Task

题意:一个机器人来回走采集信息,已知在i点采集信息需要已经有ai份信息,求最少转向次数。

简析: 模拟这个过程,每次都走到头,直到取完为止。

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 int a[1010];
 5 
 6 int main(void)
 7 {
 8     int n;
 9     scanf("%d", &n);
10     for(int i = 1; i <= n; i++) scanf("%d", a + i);
11     int s = 0, cnt = 0, ans = 0, dir = 1, pos = 1;
12     while(cnt < n)
13     {
14         if(a[pos] >= 0 && cnt >= a[pos]) cnt++, a[pos] = -1;
15         if(s && cnt < n)
16         {
17             if(pos == n) dir = -1, ans++;
18             if(pos == 1) dir = 1, ans++;
19         }
20         s = 1, pos += dir;
21     }
22     printf("%d\n", ans);
23     return 0;
24 }
参考代码

 

Problem B GCD Table

题意:有一个n元数列a[],令g(i,j) = gcd(ai, aj)得到一个n2的矩阵,让你由矩阵还原数列。

简析:论专题的重要性,这里需要用STL中的map或者Multiset。

   一.考虑到矩阵主对角线上的元素都是a[]的元素,同时一个数的因子小于等于它本身,也即是说,一个数最大的因子是自己。

   我们可以将矩阵的元素全部丢进一个Multiset多重集(不去重的set),

   第一次的时候,我们先去找里面最大的元素,该元素必然是a[]的元素,记录为a[0],再在多重集中删除它。

   然后我们再在多重集中找最大的元素,这个元素也一定是a[]的元素,因为比它大的数只有a[0]一个,所以它不是某两个不同数的公因子,而是某个数本身。

   把这个数记录为a[1],这次不仅要删除该数,还要把该数和刚才记录的a[0]的最大公因数也删除,需要注意的是要删除两次,因为g(i,j) = g(j,i)。

   接着我们再次在多重集中找最大的元素,这个元素又是a[]的元素,因为a[0]与a[1]的gcd已经被删除了,其它数的gcd小于自己,不可能最大。

   于是我们循环这个过程,不断的找多重集的最大值,然后删除它和我们已经找出的a[]中的数的gcd,直到找到全部n个数。

   看到有人用了map之类的搞,其实是一样的拉,不嫌麻烦也可以学一下这个multiset姿势。

   二.因为数字可能会比较大,而数字个数是有限的,直接开数组不现实,所以可以用map来进行存储。

   把出现的数字的出现次数记录下来,用同第一种做法相同的思路进行筛选,并最终的到结果。

Multiset代码

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <set>
 7 using namespace std;
 8 multiset<int> S;
 9 multiset<int>::iterator it;
10 vector<int> ans;
11 
12 int gcd(int a, int b)
13 {
14     return a % b ? gcd(b, a % b) : b;
15 }
16 
17 int main(void)
18 {
19     int n, x;
20     scanf("%d", &n);
21     for(int i = 0; i < n * n; i++) scanf("%d", &x), S.insert(x);
22     while(!S.empty())
23     {
24         it = S.end(), it--;
25         int cur = (*it);
26         S.erase(it);
27         for(int i = 0; i < ans.size(); i++)
28         {
29             int t = gcd(ans[i], cur);
30             for(int i = 0; i < 2; i++) it = S.find(t), S.erase(it);
31         }
32         ans.push_back(cur);
33     }
34     for(int i = 0; i < ans.size(); i++) printf("%d ", ans[i]);
35     puts("");
36     return 0;
37 }
Multiset

map代码

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<cmath>
 5 #include<map>
 6 using namespace std;
 7 int gcd(int a,int b)
 8 {
 9     if(a<b) return gcd(b,a);
10     return b==0 ? a : gcd(b,a%b);
11 }
12 
13 int cmp(int x,int y)
14 {
15     return x>y;
16 }
17 map<int,int>num;
18 int a[250005],b[505];
19 
20 int main()
21 {
22     int n;
23     scanf("%d",&n);
24     for(int i=0;i<n*n;i++)
25     {
26         scanf("%d",&a[i]);
27         num[a[i]]++;
28     }
29     sort(a,a+n*n,cmp);
30     int sum=0;
31     for(int i=0;i<n*n;i++)
32     {
33         if(num[a[i]]==0) continue;
34         num[a[i]]--;
35         for(int j=0;j<sum;j++)
36         {
37             int k=gcd(a[i],b[j]);
38             num[k]=num[k]-2;
39         }
40         b[sum]=a[i];
41         if(sum==n) break;
42         sum++;
43     }
44     for(int i=0;i<n;i++)
45     {
46         if(i!=0) printf(" ");
47         printf("%d",b[i]);
48     }
49     printf("\n");
50     
51     return 0;
52 }
map

 

1.25

Problem A Kolya and Tanya

题意:有3n 个小人坐在一个圆环上,每个小人的硬币数为 a[i],(1 <= a[i] <= 3),问满足 a[i]+a[i+n]+a[i+2n] != 6 的硬币分法有多少种

简析:如果没有不等于 6 的限制的话,总的分法一共 3^3n,为6的情况共有(123,132,213,222,231,312,321)7种

所以是 3^3n-7^n

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 typedef long long LL;
 8 const int mod = 1e9+7;
 9 int n;
10 
11 LL Q_pow(LL x,LL y){
12     LL res = 1;
13     x %= mod;
14     while(y){
15         if(y&1) res = res*x%mod;
16         x = x*x%mod;
17         y >>= 1;
18     }
19     return res;
20 }
21 
22 void solve(){
23     LL ans = (Q_pow(3,3*n) - Q_pow(7,n)+mod)%mod;
24     printf("%I64d\n",ans);
25 }
26 
27 int main(){
28     while(scanf("%d",&n) != EOF){
29         solve();
30     }
31     return 0;
32 }
参考代码

 

Problem B Marina and Vasya

题意:给出两个长度为 n 的字符串 s1,s2,构造一个与 s1 有t个字符不同,与 s2 有t个字符不同的字符串

简析:比较笨的办法,先算出 s1,s2 有 k 个位置不同

如果 k = t ,那么在这 k 个位置填上分别和 s1,s2不同的字母就可以了

如果 k > t,那么在这k个位置里面,分别有 k-t个和s1相同,k-t和s2相同,剩下的t 个填上和s1,s2不同的

如果 k < t,那么在这k个位置都填上与s1,s2不同的字母,剩下的t-k个再填上和s1或者s2不同的

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<vector>
  6 using namespace std;
  7 
  8 
  9 typedef long long LL;
 10 const int maxn = 100005;
 11 
 12 char s[maxn],t[maxn],p[maxn];
 13 int n,T,m;
 14 
 15 int check(){
 16     int c1 = 0,c2 = 0;
 17     for(int i = 1;i <= n;i++){
 18         if(s[i] != p[i]) c1++;
 19         if(t[i] != p[i]) c2++;
 20     }
 21     if(c1 != T || c2 != T) return 0;
 22     return 1;
 23 }
 24 
 25 void solve(){
 26     m = 0;    
 27     for(int i = 1;i <= n;i++){
 28         if(s[i] != t[i]) m++;
 29     }    
 30 //    printf("m = %d\n",m);
 31     
 32     if(m <= T){
 33         int cnt = 0;
 34         for(int i = 1;i <= n;i++){
 35             int u = s[i]-'a'+1;
 36             int v = t[i]-'a'+1; 
 37             if(s[i] != t[i]){        
 38             for(int j = 1;j <= 26;j++){
 39                     if(j != u && j != v){
 40                         p[i] = j-1+'a';
 41                         break;
 42                     }
 43                 }
 44             }
 45             else if(s[i] == t[i] && (cnt < T-m) ){
 46                 for(int j = 1;j <= 26;j++){
 47                     if(j != u && j != v){
 48                         p[i] = j-1+'a';
 49                         cnt++;
 50                         break;
 51                     }
 52                 }
 53                 
 54             }
 55             else p[i] = t[i];
 56         }
 57         printf("%s\n",p+1);        
 58     }
 59     
 60     else{
 61         if(T == 0 && m != 0) puts("-1");
 62         else{
 63             int c1 = 0;
 64             int c2 = 0;
 65             int f1 = 0,f2 = 0;
 66             for(int i = 1;i <= n;i++){
 67                 if(s[i] != t[i] &&(c1 < (m-T))) {
 68                     p[i] = s[i];
 69                     c1++;
 70                 }
 71                 
 72                 if(c1 == (m-T) && f1 == 0) {
 73                     f1 = 1;
 74                     continue;
 75                 }
 76                 
 77                 if(s[i] != t[i] && f1 && (c2 < m-T)){
 78                     p[i] = t[i];
 79                     c2++;
 80                 }
 81                 
 82                 if(c2 == (m-T) && f2 == 0){
 83                     f2 = 1;
 84                     continue;
 85                 }
 86                 
 87                 if(s[i] != t[i] && (c1 == m-T) && (c2 == m-T)){
 88                     int u = s[i]-'a'+1;
 89                     int v = t[i]-'a'+1;         
 90                     for(int j = 1;j <= 26;j++){
 91                     if(j != u && j != v){
 92                         p[i] = j-1+'a';
 93                         break;
 94                     }
 95                     }
 96                 }
 97                 
 98                 if(s[i] == t[i] ) p[i] = t[i];
 99                 
100             //    printf("p[%d] = %c  c1 = %d  c2 = %d\n",i,p[i],c1,c2);
101             }    
102             if(check()) printf("%s\n",p+1);
103             else puts("-1");
104         }    
105     }
106 }
107 
108 int main(){
109     while(scanf("%d %d",&n,&T) != EOF){
110         cin >> (s+1);
111         cin >> (t+1);
112         solve();
113     }
114     return 0;
115 }
参考代码
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 const int maxn = 1e5 + 10;
 7 char a[maxn], b[maxn], c[maxn];
 8 char no(char a)        {for(char i = 'a'; i <= 'z'; i++) if(i != a) return i; }
 9 char no(char a, char b){for(char i = 'a'; i <= 'z'; i++) if(i != a && i != b) return i;}
10 
11 int main(void)
12 {
13     int n, t;
14     scanf("%d %d %s %s", &n, &t, a, b);
15     int len = strlen(a), same = 0;
16     for(int i = 0; i < len; i++) same += ( a[i] == b[i] );
17     int dif = len - same, ok = 1;
18     if( (dif + 1) / 2 > t ) ok = 0;
19     else
20     {
21         int p = max(0, t-dif), q = dif - min(t, dif), r = min(t, dif) - q;
22         for(int i = 0; i < len; i++)
23         {
24             if(a[i] == b[i])
25             {
26                 if(p) c[i] = no(a[i]), p--;
27                 else c[i] = a[i];
28             }
29             else
30             {
31                 if(r) r--, c[i] = no(a[i], b[i]);
32                 else if(q) q--, c[i] = a[i];
33                 else c[i] = b[i];
34             }
35         }
36     }
37     if(ok) for(int i = 0; i < len; i++) putchar(c[i]);
38     else printf("-1");
39     puts("");
40     return 0;
41 }
参考代码

 

 

1.26

Problem A Secret Combination

题意:给出一串长度 为 n 的数字,每一次操作,可以将每个数字加 1,或者将每个数字右移一位(最右边的数字移到最左端),问得到的最小的数字

简析:可以暴力来做,因为每一个数字最多变化9次,最多只会有1000个数字,枚举每一种情况,取最小值

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 
 8 const int maxn = 1e5+5;
 9 int n;
10 char s[maxn],t[maxn];
11 char p[10][maxn];
12 
13 void solve(){
14     vector<string> c;
15 
16     for(int i = 0;i <= 9;i++){
17         for(int j = 1;j <= n;j++){
18             p[i][j] = ((s[j]-'0')+i)%10+'0';
19         }
20     }
21 
22   /*  for(int i = 0;i <= 9;i++){
23         printf("i = %d  p = %s\n",i,p[i]+1);
24     }*/
25 
26 
27     for(int i = 0;i <= 9;i++){
28         for(int k = 0;k <= n-1;k++){
29             int cnt = 0;
30             for(int l = k+1;l <= n;l++){
31                 t[++cnt] = p[i][l];
32             }
33             for(int l = 1;l <= k;l++){
34                 t[++cnt] = p[i][l];
35             }
36           //  printf("i = %d  k = %d  t = %s\n",i,k,t+1);
37             c.push_back(t+1);
38         }
39     }
40     sort(c.begin(),c.end());
41     printf("%s\n",c[0].c_str());
42 }
43 
44 int main(){
45     while(scanf("%d",&n) != EOF){
46         scanf("%s",s+1);
47         solve();
48     }
49     return 0;
50 }
参考代码

还可以贪心的来做(司老大的博客)

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 using namespace std;
 5 
 6 const int maxn = (1000 + 10)*3;
 7 char s[maxn], s1[maxn], s2[maxn];//输入的数字,当前最小数字,要比较的数字
 8 int n;
 9 
10 struct Queue
11 {
12     int pos, len;
13     Queue(int p=0, int l=0):pos(p), len(l) {}
14 }q[maxn]; //记录下连续相同数字的其实位置和个数
15 
16 bool lessthan(char* a, char* b)
17 {//比较数字a是否小于b
18     int i = 0;
19     while(i < n && a[i] == b[i]) i++;
20     if(i >= n) return false;
21     return a[i] < b[i];
22 }
23 
24 int main()
25 {
26     scanf("%d", &n);
27     scanf("%s", s);
28     strcpy(s1, s);
29     for(int i = 0; i < n; ++i) s[i+n] = s[i+2*n] = s[i];
30     int lx = 1, p = 0;
31     for(int i = 0; i < 2*n; i++)
32     {
33         int st = i;
34         int temp = 1;
35         while(i < 2*n-1 && s[i] == s[i+1])
36         {
37             temp++;
38             i++;
39         }
40         q[p++] = Queue(st, i+1-st);
41         if(temp > lx)
42             lx = temp;  //记录最长相同数字
43     }
44 
45     for(int i = 0; i < p; ++i)
46     {
47         if(q[i].len == lx)
48         {
49             int add = ('9' + 1 - s[q[i].pos]) % 10;//将这些相同数字加上add然后全部变为前导0
50             for(int j = 0; j < n; ++j)
51             {
52                 s2[j] = '0' + (s[q[i].pos+j]-'0'+add)%10;
53             }
54             if(lessthan(s2, s1))
55             {
56                 for(int k = 0; k < n; ++k) s1[k] = s2[k];
57             }
58         }
59     }
60 
61     for(int i = 0; i < n; ++i) printf("%c", s1[i]);
62     printf("\n");
63 
64     return 0;
65 }
参考代码

 

Problem B Removing Columns

题意:给出一个 n*m 的由小写字母构成的字母表,问至少删去几列,使得字母表中从上到下,是字典序递增的(不是严格递增)

简析:如果是 像这样的"a b c d e f g "这样严格上升的一列,后面不管是什么都不会再影响到字典序大小,就可以不管了

   如果是像"a b b b b c d e f" 这样有相同字母的一列,在"b b b b "这一段就无法判断,比较笨的一种办法是,把这种连续的相同字母的起点st,终点ed丢进一个vector里

   等到判断下一列的时候,就只需要再接着判断 st 到 ed 这一段

   如果是像 "g f e d c b a "这样字典序减小的一列,就必须删掉了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 
 8 typedef pair<int,int> pii;
 9 char s[105][105];
10 int n,m;
11 int lie;
12 
13 int check(int st,int ed){
14     int flag = 0;
15     for(int j = st;j <= ed;j++){
16             if(j == st) continue;
17             if(s[j][lie] < s[j-1][lie]) return 0;
18             if(s[j][lie] == s[j-1][lie]) flag = 1;
19 
20     }
21     if(flag == 1) return 2;
22     return 1;
23 }
24 
25 void solve(){
26     if(n == 1){
27         puts("0");
28         return;
29     }
30     int res = 0;
31     vector<pii> c[3];
32     lie = 1;
33     int l = 1,r = n;
34     c[0].push_back(make_pair(l,r));
35     int key = 0;
36     int tot = 0;
37     while(1){
38         int cnt = 0;
39         int lb = 0,ub = 0;
40         for(int i = 0;i < c[key].size();i++){
41             int x = c[key][i].first;
42             int y = c[key][i].second;
43           // printf("i = %d  res = %d lie = %d x = %d  y = %d\n",i,res,lie,x,y);
44             if(check(x,y) == 1) cnt++;
45             if(check(x,y) == 0){
46                 lb = 1;
47             }
48             if(check(x,y) == 2){
49                 ub = 1;
50             }
51         }
52         //printf("---cnt = %d\n",cnt);
53         if(cnt == c[key].size()) break;
54         if(lb){
55             res++;
56             c[1-key] = c[key];
57         }
58         else{
59             for(int i = 0;i < c[key].size();i++){
60                 int x = c[key][i].first;
61                 int y = c[key][i].second;
62             //   printf("i = %d  res = %d lie = %d x = %d  y = %d\n",i,res,lie,x,y);
63                 if(check(x,y) == 1) continue;
64                 if(check(x,y) == 2){
65                     for(int p = x;p <= y;){
66                         int q = p;
67                         while(q<=y && s[q][lie] == s[p][lie])q++;
68                         if(q-p > 1){
69                             c[1-key].push_back(make_pair(p,q-1));
70                         }
71                         p = q;
72                     }
73                 }
74             }
75         }
76         c[key].clear();
77         key = !key;
78         lie++;
79         if(lie == m+1) break;
80     }
81     printf("%d\n",res);
82 }
83 
84 int main(){
85     while(scanf("%d %d",&n,&m) != EOF){
86         for(int i = 1;i <= n;i++){
87             scanf("%s",s[i]+1);
88         }
89         solve();
90     }
91     return 0;
92 }
参考代码

另外可以有一种更简单的处理的办法,每判断一列的时候,严格递增的行标记成 1,之后判断到已经标记为 1 的行的时候就跳过了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 
 8 int n,m;
 9 char s[105][105];
10 
11 void solve(){
12     int c[105];
13     memset(c,0,sizeof(c));
14     int ans = 0;
15 
16     if(n == 1){
17         puts("0");
18         return;
19     }
20     for(int i = 1;i <= m;i++){
21         int ok = 0;
22         for(int j = 2;j <= n;j++){
23             if(c[j]) continue;
24             if(s[j][i] < s[j-1][i]){
25                 ok = 1;
26                 break;
27             }
28         }
29         if(ok){
30             ans++;
31         }
32         else{
33             for(int j=2;j <= n;j++){
34                 if(s[j][i] > s[j-1][i]){
35                     c[j] = 1;
36                 }
37             }
38         }
39     }
40     printf("%d\n",ans);
41 }
42 
43 int main(){
44     while(scanf("%d %d",&n,&m) != EOF){
45         for(int i = 1;i <= n;i++){
46             scanf("%s",s[i]+1);
47         }
48         solve();
49     }
50     return 0;
51 }
参考代码

 

1.27

Problem A Modular Equations

题意:求模方程aa % x=bx=b解个数。

简析:a<ba<b显然无解,a=ba=b时xx可取无穷大,infinityinfinity。

   a>ba>b时问题转化为求(ab)(a−b)大于bb的因子个数。

   用O(n0.5)O(n0.5)的复杂度枚举一下因子就可以啦。

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 
 5 int main(void)
 6 {
 7     int a, b;
 8     scanf("%d %d", &a, &b);
 9     if(a == b) puts("infinity");
10     else if(a < b) puts("0");
11     else
12     {
13         int c = a - b, ans = 0;
14         for(int i = 1; i * i <= c; i++)
15             if(c % i == 0) ans += (i > b) + (c / i > b) - (i > b && i * i == c);
16         printf("%d\n", ans);
17     }
18     return 0;
19 }
参考代码

 

Problem B Treasure

题意:在'#'处填任意个')'使满足:① 所有前缀'('个数大于等于')'个数  ②整个串'('个数等于')'个数。

简析:如果存在解,肯定可以构造成前面的所有'#'均填一个')'最后一个'#'将')'补至与'('相等。

   所以为了方便,可以先这样构造,然后check一下是否可行。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 using namespace std;
 5 char s[111111];
 6 
 7 int main(void)
 8 {
 9     scanf("%s", s);
10     int l = strlen(s), ok = 1, L = 0, R = 0, f = 0, last;
11     for(int i = 0; i < l; i++)
12     {
13         if(s[i] == '(') L++; else R++;
14         if(s[i] == '#') f++, last = i;
15         if(L < R) ok = 0;
16     }
17     int r = L - R;
18     for(int i = L = R = 0; i < l; i++)
19     {
20         if(s[i] == '(') L++; else R++;
21         if(i == last) R += r;
22         if(L < R) ok = 0;
23     }
24     if(!ok) puts("-1");
25     else
26     {
27         for(int i = 1; i < f; i++) puts("1");
28         printf("%d\n", r + 1);
29     }
30     return 0;
31 }
参考代码

 

1.28

Problem A Vasya and Wrestling

题意:给出n场比赛分别的得分,得分总数多的赢,得分总数相同的情况下,得分序列字典序大的赢,得分序列字典序相同的情况下,最后一场比赛赢的赢

简析:按照题意模拟就可以了

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<vector>
 6 using namespace std;
 7 
 8 typedef long long LL;
 9 const int maxn = 2e5+5;
10 int n;
11 int a[maxn];
12 LL lb,ub;
13 vector<int>l,r;
14 
15 void solve(){
16     if(lb > ub) {
17         puts("first");
18     }
19     else if(lb < ub){
20         puts("second");
21     }
22     else{
23         for(int i = 0;i < min(l.size(),r.size());i++){
24             if(l[i] > r[i]){
25                 puts("first");
26                 return;
27             }
28             if(r[i] > l[i]){
29                 puts("second");
30                 return;
31             }
32         }
33         if(l.size() > r.size()){
34             puts("first");
35         }
36         else if(r.size()>l.size()){
37             puts("second");
38         }
39         else{
40             if(a[n] > 0){
41                 puts("first");
42             }
43             else puts("second");
44         }
45     }
46 }
47 
48 
49 int main(){
50     while(scanf("%d",&n) != EOF){
51         lb = 0;
52         ub = 0;
53         l.clear();r.clear();
54         for(int i = 1;i <= n;i++){
55             scanf("%d",&a[i]);
56             if(a[i] > 0){
57                 lb += 1LL*a[i];
58                 l.push_back(a[i]);
59             }
60             else{
61                 ub += 1LL*(-a[i]);
62                 r.push_back(-a[i]);
63             }
64         }
65         solve();
66     }
67     return 0;
68 }
参考代码

 

Problem B Vasya and Basketball

题意:给出两支球队a 和 b 的投篮距离,确定三分线d(<= d得两分,>d 得三分),使得球队a 的总分 suma - sumb 尽量大(如果多解,输出 suma最大的情况)

简析:枚举所有的投篮距离作为 d 值,再二分求出 a 和 b 的得分,维护一个最大值(注意d 值可以为0,a 队,b队此时都是三分球)

 1 #include<iostream>  
 2 #include<cstdio>  
 3 #include<cstring> 
 4 #include <cmath> 
 5 #include<stack>
 6 #include<vector>
 7 #include<map> 
 8 #include<set>
 9 #include<queue> 
10 #include<algorithm>  
11 using namespace std;
12 
13 typedef long long LL;
14 const int INF = (1<<30)-1;
15 const int mod=1000000007;
16 const int maxn=1000005;
17 
18 int n,m;
19 int a[maxn],b[maxn],c[maxn];
20 
21 int main(){
22     int cnt=0;
23     c[++cnt]=0;
24     scanf("%d",&n);
25     for(int i=0;i<n;i++) scanf("%d",&a[i]),c[++cnt]=a[i];
26     scanf("%d",&m);
27     for(int i=0;i<m;i++) scanf("%d",&b[i]),c[++cnt]=b[i];
28     
29     sort(a,a+n);
30     sort(b,b+m);
31     sort(c,c+cnt);
32     int tmp=-INF,L,R;
33 
34     int lb,ub;
35     for(int i=1;i<=cnt;i++){
36         int x=upper_bound(a,a+n,c[i]) - a;
37         int y=upper_bound(b,b+m,c[i]) - b;        
38         
39         lb = x*2 + (n-x)*3;
40         ub = y*2 + (m-y)*3;
41         if(lb - ub > tmp){
42             tmp=lb-ub;
43             L=lb;R=ub;
44         }
45         if(lb - ub == tmp && lb > L) L = lb;
46         
47         }
48     printf("%d:%d\n",L,R);
49     return 0;
50 }
参考代码

 

1.29

Problem A Vanya and Lanterns

题意:求最小的光照范围d,使得路灯覆盖整条路。

简析:对路灯排序后,考虑覆盖左右两端,再考虑每两路灯相邻距离的一半,取最大值即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 int a[1111];
 6 
 7 int main(void)
 8 {
 9     int n, l;
10     scanf("%d %d", &n, &l);
11     for(int i = 0; i < n; i++) scanf("%d", a + i);
12     sort(a, a + n);
13     double ans = max(a[0], l - a[n-1]);
14     for(int i = 1; i < n; i++) ans = max(ans, 0.5 * ( a[i] - a[i-1] ) );
15     printf("%.9lf", ans);
16     return 0;
17 }
参考代码

 

Problem B Vanya and Exams

题意:每门考试得分aiai,多交bibi份essay可以多得1分(最高r分),要均分达到avg,最少要交几篇essay。

简析:对bb排序,从小到大,贪心的取至avg即可。

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef long long LL;
 6 
 7 struct exam
 8 {
 9     LL a, b;
10 }E[111111];
11 
12 bool cmp(exam A, exam B)
13 {
14     return A.b < B.b;
15 }
16 
17 int main(void)
18 {
19     LL n, r, avg;
20     scanf("%I64d %I64d %I64d", &n, &r, &avg);
21     for(int i = 0; i < n; i++) scanf("%I64d %I64d", &E[i].a, &E[i].b);
22     sort(E, E + n, cmp);
23     LL ans = 0LL, tot = avg * n, sum = 0LL;
24     for(int i = 0; i < n; i++) sum += E[i].a;
25     for(int i = 0; i < n; i++)
26     {
27         if(sum >= tot) break;
28         LL x = min(r - E[i].a, tot - sum);
29         sum += x, ans += E[i].b * x;
30     }
31     printf("%I64d\n", ans);
32     return 0;
33 }
参考代码

 

posted @ 2017-01-23 21:38  长安大学ACM集训队  阅读(314)  评论(0编辑  收藏  举报