一起开心寒假训练总复习

畅通工程

题意:

最少建多少道路使得两两城镇之间都能连通

题解:

利用并查集
题意很清楚,我们来分析下例子:第一行告诉你,一共有4个点,2条路。下面两行告诉你,1、3之间有条路,4、3之间有条路。那么整幅图就被分成了1-3-4和2两部分。只要再加一条路,把2和其他任意一个点连起来,畅通工程就实现了,那么这个这组数据的输出结果就是1。

那该怎么办呢?可以运用学过的并查集啊!给每个父数组一个初值-1,代表尚未与其他路接通,每连通一个路,便将-1置去,因此和普通并查集不同的地方为初始化应为如下:

for(int i=0;i<=n;i++)
    pre[i]=-1;

此时Find函数应为:

int Find(int x)
{
    
    
    if(pre[x]==-1) return x;
    return pre[x]=Find(pre[x]);

然后即可简单的写出程序,需要注意的是,需要创建的边数永远为城镇数减一,然后永远有一个根节点未置去-1,程序如下

代码:


#include<cstdio>
int pre[1010];
int find(int a)//find函数查找他的领导 
{
    
    
	int leader=a;
	while(pre[leader]!=leader)//领导不是自己,找自己的上一级领导 
		leader=pre[leader]; 
	int t,b=a;
	while(b!=leader)
	{
    
    
		t=pre[a];
		pre[a]=leader;
		b=t;
	}
	return leader;
}
int main()
{
    
    
	int n,m,point1,point2,all,lead1,lead2;//point表示城镇编号 
	while(scanf("%d",&n),n)
	{
    
    
		all=n-1;//n个点不连成环需要n-1条边 
		for(int i=1;i<=n;i++){
    
    
			pre[i]=i;//初始化为自己的领导是自己 
		}
		scanf("%d",&m);
		for(int j=0;j<m;j++){
    
    
			scanf("%d%d",&point1,&point2);
			lead1=find(point1);
			lead2=find(point2);
			if(lead1!=lead2){
    
    
				pre[lead2]=lead1;
				all--;//根据题目给出的条件每连通一条总数减一 
			}
		}
		printf("%d\n",all);
	}
	return 0;
}

小希的迷宫

题解:

该题用并查集做难度不大,判断是否成环即判断是否要合并的两个结点的根结点相同即可,因此稍稍改变了一下join函数。
需要注意的是还有一点是每个房间必须连通,这一点我一开始忽视了于是过样例却WA(因为样例都是保证全连通);

代码:

#include<bits/stdc++.h>
using namespace std;
int pre[100005];
bool vis[100005];

int find(int x){
    
    
    int r = x;
    while(pre[r] != r){
    
    
        r = pre[r];
    }
    int i = x, j;
    while(i != r){
    
    
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}

bool join(int x, int y){
    
    
    int i = find(x), j = find(y);
    if (i != j){
    
    
        pre[j] = i;
        return true;
    }
    else return false;
}

int main()
{
    
    
    int a, b;
    while(scanf("%d%d", &a, &b) != EOF){
    
    
        for (int i = 1; i <= 100005; ++i) pre[i] = i;
        memset(vis, 0, sizeof(vis));
        if (a == -1 && b == -1) break;
        if (a == 0 && b == 0){
    
    
            printf("Yes\n");
            continue;
        }
        bool flag = 1;
        while(a != 0){
    
    
            vis[a] = vis[b] = 1;
            //判断是否成环
            if(!join(a, b)) flag = 0;
            scanf("%d%d", &a, &b);
        }
        int root = 0;
        //判断是否全连通
        for (int i = 1; i <= 100005; ++i)
            if (vis[i] && pre[i] == i) ++root;
        if (root > 1) flag = 0;
        if (flag) printf("Yes\n");
        else printf("No\n");
    }

    return 0;
}

Express Mail Taking

题意:

现在有n个柜子依次排列,这些柜子编号从1~n。每个柜子之间距离为1 ,其中柜子编号为k的是特殊的,这是个密码柜,要向打开其他柜子就必须在这个柜子输入密码。现在你有m个快递在这些柜子中,你从入口进入,取完快递后从入口出来,问你需要走的最小距离。

题解:

贪心做法:
由于这些快递这件是没有关联的,所以这道题是非常简单的,在没有取完快递之前我们都需要先去密码柜再输入密码再去取快递,这些步骤都是等效的。 那么这道题为什么会有最小距离呢?重要的就是在取最后一个快递的时候,我们取完最后一个快递是不是没必要回到密码柜了,直接走到出口,故:最后一个快递的位置显得极为重要,这也是决定性因素。所以我们想要让最后一个快递的位置靠近入口即可。此题则易解。

代码:

#include<bits/stdc++.h>	//POJ不支持

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e6;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为自定义代码模板***************************************//

ll t,n,m,k;
ll a[maxn];
int main(){
    
    
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>t){
    
    
		while(t--){
    
    
			cin>>n>>m>>k;
			rep(i,0,m-1){
    
    
				cin>>a[i];
			}
			sort(a,a+m);
			ll ans=k-1;
			per(i,m-1,1){
    
    
				ans+=abs((a[i]-k)*2);
			}
			if(a[0]<=k)
				ans+=k-1;
			else{
    
    
				ans+=abs((a[0]-k)*2)+k-1;
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}

Reports

题意:

给你某一天的报告,在这一天总共报告n nn次,其中1 11表示入校,0 00表示离校,当且仅当不连续存在两个相同的报告类型时这份报告才算正确。现在请你判断这份报告是否有误。

题解:

根据题意,直接判断是否有相邻元素相等就行

代码:

#include<bits/stdc++.h>	

#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define IOS ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define fi first
#define se second
#define mp make_pair

using namespace std;

const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 1e5;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;

int t,n;
int a[maxn];
int main(){
    
    
	//freopen("in.txt", "r", stdin);//提交的时候要注释掉
	IOS;
	while(cin>>t){
    
    
		while(t--){
    
    
			cin>>n;
			rep(i,0,n-1){
    
    
				cin>>a[i];
			}
			bool flag=false;
			rep(i,0,n-2){
    
    
				if(a[i]==a[i+1]){
    
    
					flag=true;
					break;
				}
			}
			if(flag)
				cout<<"NO"<<endl;
			else
				cout<<"YES"<<endl;
		}
	}
	return 0;
}

放苹果

题意:

把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?(用K表示)5,1,1和1,5,1 是同一种分法。

题解:

:首先,设 i个苹果放在 k个盘子里的放法总数是 f(i,k) ,分类讨论有两种情况
(1)当 k > i 时,f ( i , k ) = f ( i , i )
(2)当 k <= i时,总放法 = 有盘子为空的放法+没盘子为空的放法
f ( i , k ) = f ( i , k - 1 ) + f ( i - k , k )

代码:

#include<iostream>
using namespace std;
//设 i个苹果放在 k个盘子里的放法总数是 f(i,k) 
int f(int i,int k){
    
    
    if(k > i){
    
    //当 k > i时,f(i,k) = f(i,i) 
        return f(i,i);
    }
    if(i == 0)
        return 1;
    if(k == 0){
    
    
        return 0;
    }
    //k <= i时,总放法 = 有盘子为空的放法+没盘子为空的放法
    //f(i,k) = f(i,k-1) + f(i-k,k) 
    return f(i,k-1)+f(i-k,k);
}

int main(){
    
    
    int t,i,k,count=0;
    cin>>t;
    while(t--){
    
    
        cin>>i>>k; 
        cout<<f(i,k)<<endl;
    }
    return 0;
} 

Monthly Expense

题意:

给出农夫在n天中每天的花费,要求把这n天分作m组,每组的天数必然是连续的,要求分得各组的花费之和应该尽可能地小,最后输出各组花费之和中的最大值

题解:

各组最小和的最大值应该用二分做,枚举一个答案,然后验证是否合理,不断缩小范围最后确定答案

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<string>
#include<cstdlib>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define N 100001
#define MOD 123
#define E 1e-6
using namespace std;
int n,m;
int a[N];
bool judge(int value)//判断当前花费可把n分成几组
{
    
    
    int sum=0;//花费总和
    int cnt=1;//初始时只有一组
    for(int i=0;i<n;i++)//枚举每天花费
    {
    
    
        if(sum+a[i]<=value)
            sum+=a[i];
        else
        {
    
    
            sum=a[i];
            cnt++;
        }
    }
    if(cnt>m)//若分组数比规定数要多,则mid偏小,需调整左值
        return false;
    else//若分组数比规定数要少,则mid偏大,需调整右值
        return true;
}
int main()
{
    
    
    while(scanf("%d%d",&n,&m)!=EOF)
    {
    
    
        int left=0,right=0;
        for(int i=0;i<n;i++)
        {
    
    
            scanf("%d",&a[i]);
            left=max(left,a[i]);
            right+=a[i];
        }
        int mid;
        while(left<=right)
        {
    
    
            mid=(left+right)/2;
            if(judge(mid))
                right=mid-1;
            else
                left=mid+1;
        }
        printf("%d\n",mid);
    }
    return 0;
 }

String Typing

题意:

给一个字符串,可以复制某一段字符,问最少需要多少步能将其输出,比如abcabcd,先输入abc然后再赋值abc再输入d就只需要5步。
复制的这段字符 必须是从字符串的0位置开始复制的 而且只能粘贴一次 例abcabcabc 输出为7

题解:

string中的str.substr(i,j) 截取字符串str第i位置开始的长度为j的一段字符
从开始截取长度为i的一段,看是否和后面有重复的

代码:

#include <bits/stdc++.h>
//freopen("1.txt", "r", stdin);
using namespace std;
const int maxn = 10010, INF = 0x7fffffff;

int main()
{
    
    
    int n;
    string str;
    cin>> n >> str;
    int res = n;
    for(int i=1; i<=n/2; i++)
        if(str.substr(0, i) == str.substr(i, i)) res = n - i + 1;
    cout<< res <<endl;

    return 0;
}

Diagonal Walking

题意:

给出n个操作,相连的U和R 可以看为一步,问一共多少步

题解:

直接按照题目模拟即可,遇到UR或者RU就跳过,其他的计算步数

代码:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int main(){
    
    
	int n;
	cin >> n;
	string s;
	cin >> s;
	int u = 0, r = 0, ans = 0;
	int len = s.size();
	for(int i = 0; i < s.size(); i++){
    
    
		if((s[i] == 'U' && s[i+1] == 'R') || (s[i] == 'R' && s[i+1] == 'U')){
    
    
			i++;  // 相连的U和R看为一步
		}
		ans++;
	}
	cout << ans << endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35975367/article/details/114115004