ACM寒假欢乐赛 题解

文章目录[x]
  1. 1:原题链接:
  2. 2:题意:
  3. 3:思路:
  4. 4:标程:
  5. 5:原题链接:
  6. 6:题意:
  7. 7:思路:
  8. 8:标程:
  9. 9:原题链接:
  10. 10:题意:
  11. 11:思路;
  12. 12:标程:
  13. 13:原题链接:
  14. 14:题意:
  15. 15:思路:
  16. 16:标程:
  17. 17:原题链接:
  18. 18:题意:
  19. 19:思路:
  20. 20:标程:
  21. 21:原题链接:
  22. 22:思路:
  23. 23:标程:
  24. 24:原题链接:
  25. 25:题意:
  26. 26:思路;
  27. 27:标程:
  28. 28:原题链接:
  29. 29:题意:
  30. 30:思路:
  31. 31:标程:
  32. 32:原题链接:
  33. 33:题意:
  34. 34:思路:
  35. 35:标程:
  36. 36:原题链接:
  37. 37:题意:
  38. 38:思路;
  39. 39:标程:


 

比赛网址

https://vjudge.net/contest/350953

首先:因为突然去世,导致体验极差,等活过来就可以看到提交结果了(这谁想得到呢,昨天还是活的)
在这里插入图片描述

A.1228

原题链接:

https://codeforces.com/problemset/problem/1228/A

题意:

给定一个区间),让你取区间中任意一个数,要求的每一位数字都不相同,不存在这样的数则输出

思路:

暴力判断即可。

标程:

#include <bits/stdc++.h>
using namespace std;
int main() {
  int l, r;
  scanf("%d%d", &l, &r);
  for (int i = l; i <= r; ++i) {
    int book[15] = {0}, temp = i, flag = 0;
    while (temp) {
      book[temp % 10]++;
      if (book[temp % 10] == 2) {
        flag = 1;
        break;
      }
      temp /= 10;
    }
    if (!flag)
      return !printf("%d\n", i);
  }
  return !printf("-1\n");
}

 

B.1118

原题链接:

https://codeforces.com/contest/1118/problem/B

题意:

个数,问依次删去一个数后,剩下的数的奇数位置上的和和偶数位置上的和相等的有多少个。

思路:

是对于删除第位数的话,第位之前的奇偶性是不变的,第位之后的奇偶性是和原来相反的,所以我们用前缀和,分别求出奇数位置上的前缀和和偶数位置上的前缀和,然后对于第位删除掉了以后就是判断一下前位的奇数前缀和加上后面偶数位的前缀和是否等于前位的偶数前缀和加上后面奇数位置的前缀和。

标程:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5 + 10;
int a[N], odd[N], even[N];
int main() {
  int n;
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i]);
  }
  odd[1] = a[1];
  even[1] = 0;
  for (int i = 2; i <= n; i++) {
    if (i & 1) {
      odd[i] = a[i] + odd[i - 1];
      even[i] = even[i - 1];
    } else {
      odd[i] = odd[i - 1];
      even[i] = even[i - 1] + a[i];
    }
  }
  int ans = 0;
  for (int i = 1; i <= n; i++) {
    if (odd[i - 1] + even[n] - even[i] == even[i - 1] + odd[n] - odd[i]) {
      ans++;
    }
  }
  printf("%d\n", ans);
  return 0;
}

C.1559

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1559

题意:

给你一个的整数矩阵,在上面找一个的子矩阵,使子矩阵中所有元素的和最大。

思路;

预处理二维前缀和然后查询就行了。

标程:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;
ll a[N][N];
int main() {
  int t, m, n, x, y;
  ll ans;
  scanf("%d", &t);
  while (t--) {
    ans = -inf;
    scanf("%d%d%d%d", &n, &m, &x, &y);
    for (int i = 1; i <= n; i++) {
      for (int j = 1; j <= m; j++) {
        scanf("%lld", &a[i][j]);
        a[i][j] = a[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
      }
    }
    //从i,j开始 到 i+x-1,j+y-1
    for (int i = 1; i <= m - x + 1; i++) {
      for (int j = 1; j <= n - y + 1; j++) {
        ans = max(ans, a[i + x - 1][j + y - 1] - a[i + x - 1][j - 1] - a[i - 1][j + y - 1] + a[i - 1][j - 1]);
      }
    }
    printf("%lld\n", ans);
  }
  return 0;
}

D.2456

原题链接:

http://poj.org/problem?id=2456

题意:

个地方,有头牛,一头牛只能分配一个地方,问如何分配使得所有牛之间的距离最小的那个值最大,是多少。

思路:

求最小值的最大值,先对隔间的位置进行排序,隔间之间的最小值最大为最后那个隔间的位置减去第一个隔间的位置,这样的话我们就可以二分了,判断每俩头牛之间最小相距距离时,能否装下头牛,如何判断也是一个值得注意的地方,肯定要把第一个隔间的位置放一头牛,贪心的思想,然后判断下一个隔间的位置与上一个放牛的位置的距离是否大于等于,符合条件则放入,并把上一个放牛的位置改为当前位置。

标程:

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
int judge(int x) {
    int num=1;
    int pos=a[1];
    for(int i=2; i<=n; i++) {
        if(a[i]-pos>=x) {
            num++;
            pos=a[i];
        }
    }
    return num>=m;
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) {
        scanf("%d",&a[i]);
    }
    sort(a+1,a+1+n);
    int l=0,r=1e9;
    int ans;
    while(l<=r) {
        int mid=(l+r)/2;
        if(judge(mid)) {
            l=mid+1;
            ans=mid;
        } else {
            r=mid-1;
        }
    }
    printf("%d\n",ans);
    return 0;
}

E.1988

原题链接:

http://poj.org/problem?id=1988

题意:

给定个方块,排成一行,将它们编号。再给出个操作:

表示将i所在的那一堆移到j所在那一堆的顶上。

表示一个询问,询问下面有多少个方块。

思路:

带权并查集,一堆中最顶上的方块作为父节点,用 统计X到父亲节点的距离,表示这个集合的大小,两者相减即为答案。

标程:

#include <bits/stdc++.h>
using namespace std;

#define ll long long
const int inf = 0x3f3f3f3f;
const int N = 300010;
const double pi = acos(-1.0);

int pre[N],num[N],dis[N];
int n;

void init() {
    for(int i=0; i<n; i++) {
        pre[i]=i;
        num[i]=1;
        dis[i]=0;
    }
}
int Find(int x) {
    if(x!=pre[x]) {
        int t=pre[x];
        pre[x]=Find(pre[x]);
        dis[x]+=dis[t];
    }
    return pre[x];
}

int main() {
    while(~scanf("%d",&n)) {
        init();
        char op;
        while(n--) {
            scanf(" %c",&op);
            int x,y;
            if(op=='M') {
                scanf("%d %d",&x,&y);
                int fx=Find(x);
                int fy=Find(y);
                if(fx!=fy) {
                    pre[fy]=fx;
                    dis[fy]=num[fx];
                    num[fx]+=num[fy];
                }
            } else {
                scanf("%d",&x);
                int fx=Find(x);
                printf("%d\n",num[fx]-dis[x]-1);
            }
        }
    }
    return 0;
}

F.1421

原题链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1421

题意:

()个数中选出k对数(即个),使它们的差的平方和最小。

思路:

显然,如果要让疲劳度最低那么一定要选择重量相邻的,所以要对所有的物品按照重量进行一次排序

表示有个物品,从中挑对物品的最小可能疲劳度,显然这个状态只能来自于两种,分别处理。

状态转移方程式为:

:且

标程:

#include<bits/stdc++.h>
using namespace std;
const int inf = 0xfffffff;
const int N = 2e3+10;
int a[N],dp[N][N];
int main() {
    int n,k;
    while(cin>>n>>k) {
        for(int i=0; i<n; i++) {
            cin >> a[i];
        }
        for(int i=0; i<=n; i++) {

            for(int j=0; j<=k; j++) {
                j == 0 ? dp[i][j] = 0 :dp[i][j] = inf;
            }
        }
        sort(a,a+n);
        for(int i=2; i<=n; i++) {
            for(int j=1; j<=k&&j<=i; j++) {
                dp[i][j] = min(dp[i-1][j], dp[i-2][j-1] + (a[i-1]-a[i-2])*(a[i-1]-a[i-2]));
            }
            cout << dp[n][k] << endl;
        }
    }
    return 0;
}

G.1376

原题链接:

http://poj.org/problem?id=1376

题意:

给定地图、起点、终点、初始方向,从起点开始每次操作能够向当前方向前进1~3格或者旋转90度改变方向,问最少需要几次操作才能到达终点。

思路;

先转换地图,开一个数组,当遇到不能走的方格的时候即输入值为的时候,就把此时的看成点,,这样标记完成以后,这个面就变成了一个个点,并且此时的机器人是有半径的,所以在走的时候不可能走到边界上,然后就行了。要注意一次能走格。

标程:

#include<stdio.h>
#include<queue>
#include<string.h>
#include<algorithm>
using namespace std;
struct note {
    int x,y,id,s;
    bool operator == (const note &a) const {
        return a.y==y&&a.x==x;
    }
} st,ed;
int vis[100][100][10];
int mp[100][100];
int n,m;
int dir[4][2]= {0,1,1,0,0,-1,-1,0};
int check(int x,int y,int id) {
    if(x>0&&y>0&&x<n&&y<m&&vis[x][y][id]==0)
        return 1;
    return 0;
}
int bfs() {
    queue<note>Q;
    memset(vis,0,sizeof(vis));
    st.s=0;
    Q.push(st);
    note now,temp;
    while(!Q.empty()) {
        now=Q.front();
        Q.pop();
        if(now==ed) {
            printf("%d\n",now.s);
            return 1;
        }

        temp=now;
        temp.s++;
        temp.id=(temp.id+1)%4;
        if(vis[temp.x][temp.y][temp.id]==0) {
            vis[temp.x][temp.y][temp.id]=1;
            Q.push(temp);
        }
        temp=now;
        temp.s++;
        temp.id=(temp.id-1+4)%4;
        if(vis[temp.x][temp.y][temp.id]==0) {
            vis[temp.x][temp.y][temp.id]=1;
            Q.push(temp);
        }

        for(int i=1; i<=3; i++) {
            temp=now;
            temp.x=now.x+dir[temp.id][0]*i;
            temp.y=now.y+dir[temp.id][1]*i;
            if(mp[temp.x][temp.y]==1)break;
            if(check(temp.x,temp.y,temp.id)) {
                temp.s++;
                vis[temp.x][temp.y][temp.id]=1;
                Q.push(temp);
            }
        }
    }
    return 0;
}
int main() {
    while(~scanf("%d%d",&n,&m)&&(n+m)) {
        int t;
        memset(mp,0,sizeof(mp));
        for(int i=0; i<n; i++) {
            for(int j=0; j<m; j++) {
                scanf("%d",&t);
                if(t==1) {
                    mp[i][j]=mp[i+1][j]=mp[i][j+1]=1;
                    mp[i+1][j+1]=1;
                }
            }
        }
        scanf("%d%d%d%d",&st.x,&st.y,&ed.x,&ed.y);
        char mp[20];
        scanf("%s",mp);
        if(mp[0]=='s')
            st.id=1;
        else if(mp[0]=='e')
            st.id=0;
        else if(mp[0]=='w')
            st.id=2;
        else
            st.id=3;
        if(bfs()==0)
            printf("-1\n");
    }
    return 0;
}

H.6221

原题链接:

https://codeforces.com/problemset/problem/622/A

题意:

一个序列是这样排的,求第个是什么数字。

思路:

个位置属于,求出,然后()就是答案了。

方法1:可以枚举,当()大于等于时,就是了。

方法2:先让,如果(),那正确的应该

标程:

  • 方法
#include <cstdio>
typedef long long ll;
int main() {
    ll n,k=1;
    scanf("%lld",&n);
    while(k) {
        if(k*(k+1)/2>=n)break;
        k++;
    }
    printf("%lld\n",n-k*(k-1)/2);
    return 0;
}
  • 方法
#include<cstdio>
#include<cmath>
typedef long long ll;
int main() {
    ll n,k;
    scanf("%lld",&n);
    k=floor(sqrt(2*n));
    if (k*(k+1)/2<n)k++;
    printf("%lld",n-k*(k-1)/2);
    return 0;
}

 

I.1263

原题链接:

https://codeforces.com/problemset/problem/1263/C

题意:

给一个让你找任意数向下取整可以得到的值。

思路:

所以我们暴力枚举肯定是不超时的,但是数组会存不下,这里我们会发现一个规律就是枚举到i是的值一样时,后面所有的都会存在。

标程:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7;
int ans[N];
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        ll n;
        scanf("%lld",&n);
        int cut=0;
        for(int l=1,r; l<=n; l=r+1) {
            r=n/(n/l);
            ans[cut++]=n/l;
        }
        ans[cut++]=0;
        printf("%d\n",cut);
        sort(ans,ans+cut);
        for(int i=0; i<cut; ++i) {
            printf("%d ",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

J.1278

原题链接:

https://codeforces.com/problemset/problem/1278/B

题意:

对于个测试点,给两个数,作如下操作:

步可以挑一个数字加 ,问你最少多少步能让两个数字相等。

思路;

假设操作了次。那么我们要保证

假设最后加到了另外 ==

应该是一个偶数这时候应该加了=
这时候只需要保证为偶数就有解。因为有相同的奇偶性,所以只需要满足,并且为偶数即可,所以只需要从开始遍历,满足条件即可输出答案。

标程:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        ll a,b;
        scanf("%lld%lld",&a,&b);
        for(ll i=0;; i++) {
            ll temp=(i+1)*i/2;
            if(temp>=abs(a-b)&&(a+b+temp)%2==0) {
                printf("%lld\n",i);
                break;

            }
        }
    }
    return 0;
}

再次声明,此次比赛为寒假欢乐赛,旨在提高大家刷题兴趣和督促大家学习,并不影响大家的正常集训学习。

 

点赞
  1. SEO Reseller说道:

    Awesome post! Keep up the great work! :)

发表评论

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像