ACM寒假欢乐赛 题解

比赛网址

https://vjudge.net/contest/350953

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

A.1228

原题链接:

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

题意:

给定一个区间 1 l r 1 0 5 1\leq l \leq r \leq 10^5 ),让你取区间中任意一个数 x x ,要求 x x 的每一位数字都不相同,不存在这样的数则输出 1 -1

思路:

暴力判断即可。

标程:

#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

题意:

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

思路:

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

标程:

#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

题意:

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

思路;

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

扫描二维码关注公众号,回复: 9394349 查看本文章

标程:

#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

题意:

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

思路:

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

标程:

#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

题意:

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

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

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

思路:

带权并查集,一堆中最顶上的方块作为父节点,用 d i s [ X ] dis[X] 统计X到父亲节点的距离, n u m [ f a [ X ] ] num[fa[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

题意:

n n ( n 2000 n \leq 2000 )个数中选出k对数(即 2 k 2*k 个),使它们的差的平方和最小。

思路:

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

d p [ i ] [ j ] dp[i][j]表示有 i i 个物品,从中挑 j j 对物品的最小可能疲劳度,显然这个状态只能来自于 d p [ i 1 ] [ j ] dp[i−1][j] d p [ i 2 ] [ j 1 ] dp[i-2][j-1] 两种,分别处理。

状态转移方程式为:

d p [ i ] [ j ] = m i n ( d p [ i 1 ] [ j ] , d p [ i 2 ] [ j 1 ] + ( a [ i 1 ] a [ i 2 ] ) ( a [ i 1 ] a [ i 2 ] ) ) 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]))

i : 2 > n i:2->n j 1 > k j i j: 1->k且j \leq i

标程:

#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度改变方向,问最少需要几次操作才能到达终点。

思路;

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

标程:

#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 , 1 , 2 , 1 , 2 , 3 , 1 , 2 , 3 , 4 , 1 , 2 , 3 , 4 , 5.... 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5.... 这样排的,求第 n n 个是什么数字。

思路:

n n 个位置属于 1 1 k k ,求出 k k ,然后 n k k 1 2 n-\frac{k*(k-1)}{2} 就是答案了。

方法1:可以枚举 k k i i ,当 i i 1 2 \frac{i*(i-1)}{2} 大于等于 n n 时, k k 就是 i i 了。

方法2:先让 k = f l o o r ( s q r t ( 2 n ) ) k=floor(sqrt(2*n)) ,如果 k k 1 2 < n \frac{k*(k-1)}{2}<n ,那正确的 k k 应该 + 1 +1

标程:

  • 方法 1 1
#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;
}
  • 方法 2 2
#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

题意:

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

思路:

n n 1 e 9 1e9 所以我们暴力枚举肯定是不超时的,但是数组会存不下,这里我们会发现一个规律就是枚举到i是 n i \frac{n}{i} n i 1 \frac{n}{i-1} 的值一样时,后面所有的 0 n i 0到\frac{n}{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

题意:

对于 t ( 1 t 100 ) t(1 \leq t \leq 100) 个测试点,给两个数 a a b b ,作如下操作:

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

思路;

假设操作了 x x 次。那么我们要保证$\frac{ (x+1)*x}{2} \leq abs(a-b) $。

假设最后 a b a,b 加到了 y y 另外 a + b + ( x + 1 ) x 2 a+b+\frac{(x+1)*x}{2} == 2 y 2*y

应该是一个偶数这时候 b b 应该加了 ( a + b + ( x + 1 ) x 2 ) / 2 b (a+b+\frac{(x+1)*x}{2})/2-b = ( ( x + 1 ) x 2 + a b ) / 2 (\frac{(x+1)*x}{2}+a-b)/2
这时候只需要保证为偶数 ( x + 1 ) x 2 + a b \frac{(x+1)*x}{2}+a-b 就有解。因为 a + b a+b a b a-b 有相同的奇偶性,所以只需要满足$abs(a-b)\leq \frac{(x+1)*x}{2} ,并且 \frac{(x+1)*x}{2} +a+b$为偶数即可,所以只需要从 0 0 开始遍历 x x ,满足条件即可输出答案。

标程:

#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;
}

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

发布了311 篇原创文章 · 获赞 226 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/nuoyanli/article/details/103940440
今日推荐