ACM寒假歡樂賽 題解

比賽網址

https://vjudge.net/contest/350953

首先:因爲cfcf突然去世,導致體驗極差,等cfcf活過來就可以看到提交結果了(這誰想得到呢,昨天還是活的)
在這裏插入圖片描述

A.1228

原題鏈接:

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

題意:

給定一個區間1lr1051\leq l \leq r \leq 10^5),讓你取區間中任意一個數xx,要求xx的每一位數字都不相同,不存在這樣的數則輸出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

題意:

nn個數,問依次刪去一個數後,剩下的數的奇數位置上的和和偶數位置上的和相等的有多少個。

思路:

是對於刪除第ii位數的話,第ii位之前的奇偶性是不變的,第ii位之後的奇偶性是和原來相反的,所以我們用前綴和,分別求出奇數位置上的前綴和和偶數位置上的前綴和,然後對於第ii位刪除掉了以後就是判斷一下前i1i-1位的奇數前綴和加上後面偶數位的前綴和是否等於前i1i-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×nm×n的整數矩陣,在上面找一個x×yx×y的子矩陣,使子矩陣中所有元素的和最大。

思路;

預處理二維前綴和然後O(1)O(1)查詢就行了。

標程:

#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

題意:

nn個地方,有mm頭牛,一頭牛隻能分配一個地方,問如何分配使得所有牛之間的距離最小的那個值最大,是多少。

思路:

求最小值的最大值,先對隔間的位置進行排序,隔間之間的最小值最大爲最後那個隔間的位置減去第一個隔間的位置,這樣的話我們就可以二分了,判斷每倆頭牛之間最小相距midmid距離時,能否裝下cc頭牛,如何判斷也是一個值得注意的地方,肯定要把第一個隔間的位置放一頭牛,貪心的思想,然後判斷下一個隔間的位置與上一個放牛的位置的距離是否大於等於midmid,符合條件則放入,並把上一個放牛的位置改爲當前位置。

標程:

#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

題意:

給定NN個方塊,排成一行,將它們編號11NN。再給出PP個操作:

MM ii jj表示將i所在的那一堆移到j所在那一堆的頂上。

CC ii表示一個詢問,詢問ii下面有多少個方塊。

思路:

帶權並查集,一堆中最頂上的方塊作爲父節點,用dis[X]dis[X] 統計X到父親節點的距離,num[fa[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

題意:

nn(n2000n \leq 2000)個數中選出k對數(即2k2*k個),使它們的差的平方和最小。

思路:

顯然,如果要讓疲勞度最低那麼一定要選擇重量相鄰的,所以要對所有的物品按照重量進行一次排序

dp[i][j]dp[i][j]表示有ii個物品,從中挑jj對物品的最小可能疲勞度,顯然這個狀態只能來自於dp[i1][j]dp[i−1][j]dp[i2][j1]dp[i-2][j-1]兩種,分別處理。

狀態轉移方程式爲:

dp[i][j]=min(dp[i1][j],dp[i2][j1]+(a[i1]a[i2])(a[i1]a[i2]))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>ni:2->nj1>kjij: 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度改變方向,問最少需要幾次操作才能到達終點。

思路;

先轉換地圖,開一個數組,當遇到不能走的方格的時候即輸入值爲11的時候,就把此時的iijj看成點,mp[i][j]=mp[i+1][j]=mp[i][j+1]=mp[i+1][j+1]mp[i][j]=mp[i+1][j]=mp[i][j+1]=mp[i+1][j+1],這樣標記完成以後,這個面就變成了一個個點,並且此時的機器人是有半徑的,所以在走的時候不可能走到邊界上,然後bfsbfs就行了。要注意一次能走131-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....這樣排的,求第nn個是什麼數字。

思路:

nn個位置屬於11kk,求出kk,然後nkk12n-\frac{k*(k-1)}{2}就是答案了。

方法1:可以枚舉kkii,當ii12\frac{i*(i-1)}{2}大於等於nn時,kk就是ii了。

方法2:先讓k=floor(sqrt(2n))k=floor(sqrt(2*n)),如果kk12<n\frac{k*(k-1)}{2}<n,那正確的kk應該+1+1

標程:

  • 方法11
#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;
}
  • 方法22
#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

題意:

給一個nn讓你找n\frac{n}{任意數}向下取整可以得到的值。

思路:

nn1e91e9所以我們暴力枚舉肯定是不超時的,但是數組會存不下,這裏我們會發現一個規律就是枚舉到i是ni\frac{n}{i}ni1\frac{n}{i-1}的值一樣時,後面所有的0ni0到\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(1t100)t(1 \leq t \leq 100)個測試點,給兩個數aabb,作如下操作:

ii 步可以挑一個數字加 ii ,問你最少多少步能讓兩個數字相等。

思路;

假設操作了xx次。那麼我們要保證$\frac{ (x+1)*x}{2} \leq abs(a-b) $。

假設最後aba,b加到了yy另外a+b+(x+1)x2a+b+\frac{(x+1)*x}{2} ==2y2*y

應該是一個偶數這時候bb應該加了(a+b+(x+1)x2)/2b(a+b+\frac{(x+1)*x}{2})/2-b=((x+1)x2+ab)/2(\frac{(x+1)*x}{2}+a-b)/2
這時候只需要保證爲偶數(x+1)x2+ab\frac{(x+1)*x}{2}+a-b就有解。因爲a+ba+baba-b有相同的奇偶性,所以只需要滿足$abs(a-b)\leq \frac{(x+1)*x}{2} ,並且\frac{(x+1)*x}{2} +a+b$爲偶數即可,所以只需要從00開始遍歷xx,滿足條件即可輸出答案。

標程:

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

再次聲明,此次比賽爲寒假歡樂賽,旨在提高大家刷題興趣和督促大家學習,並不影響大家的正常集訓學習。

發佈了305 篇原創文章 · 獲贊 222 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章