文章目录
畅通工程
题意:
最少建多少道路使得两两城镇之间都能连通
题解:
利用并查集
题意很清楚,我们来分析下例子:第一行告诉你,一共有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;
}