训练赛2(小)
导语
小型训练赛,虽然不用交整理,但是自己还是想整理一下,毕竟题目简单都没做完
涉及的知识点
贪心、思维、字符串、树、差分
题目
A(CodeForces 996A)
题目大意:给出一个数,尝试用1,5,10,20,100的硬币并且用最少的个数分完
思路:贪心,每次用面值最大的分到不能分为止,换更小的一个
代码
#include <bits/stdc++.h>
using namespace std;
long long n,data[5]= {1,5,10,20,100},ans;
int main() {
cin >>n;
for(int i=4; i>=0; i--)
if(n) {
ans+=n/data[i];
n%=data[i];
}
cout <<ans;
return 0;
}
B(CodeForces 991A)
题目大意:A个学生去过1地,B个学生去过2地,C个学生两地都去过,N为学生总个数,没有去过1地和2地的至少有一人,判断给出的数是否满足这样的条件
思路:直接用集合论判断,注意特殊情况,比如C ≥ \ge ≥A或C ≥ \ge ≥B
代码
#include <bits/stdc++.h>
using namespace std;
int A,B,C,N,pass;
int main() {
cin >>A>>B>>C>>N;
N-A-B+C>=1&&C<=A&&C<=B?cout <<N-A-B+C:cout <<"-1";
return 0;
}
C(CodeForces 989B)
题目大意:给出一个由0、1、.构成的字符串,给出串的长度n和一个数p,.可以被替换成0或1,当对所有的1 ≤ \le ≤i ≤ \le ≤n-p,第i个和第i+p个字符如果都相等则认为该字符串为p字符串,对于给定的0、1、.构成的字符串,判断能否组成为p字符串
思路:直接模拟即可,注意判断如果一开始所有i和i+p都相等了,就不可能有非p字符串
代码
#include <bits/stdc++.h>
using namespace std;
int n,p,ans;
char str[2121];
bool flag;
int main() {
scanf("%d%d",&n,&p);
getchar();
scanf("%s",str+1);
for(int i=1; i<=n-p; i++)
if(str[i]==str[i+p]) {//找到了直接变
if(str[i]=='.') {
str[i]='1',str[i+p]='0';
flag=1;
break;
}
} else {
if(str[i]=='.') {
if(str[i+p]=='1')
str[i]='0';
else if(str[i+p]=='0')
str[i]='1';
} else if(str[i+p]=='.') {
if(str[i]=='1')
str[i+p]='0';
else if(str[i]=='0')
str[i+p]='1';
}//能变直接变,只需要有一对地方不同即可
flag=1;
break;
}
if(!flag)
cout <<"No";
else
for(int i=1; i<=n; i++)
if(str[i]=='.')
printf("1");
else
printf("%c",str[i]);
return 0;
}
D(CodeForces 978E)
题目大意:有一辆公交车,经过n个站,给出最大容量w,给出每个站公交车上数量相对于前一个站的公交车数量的变化值,求出最开始公交车上的人数有多少种可能
思路:贪心,获取可能人数的最大上限和最小下限,相减即是答案,注意排除特殊情况
代码
#include <bits/stdc++.h>
using namespace std;
long long n,w,a[1212],H,L,ans;
bool flag;
int main() {
scanf("%lld%lld",&n,&w);
H=w;
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(int i=1; i<=n; i++) {
ans+=a[i];
if(ans>0)
H=min(H,w-ans);//取最大上界
else if(ans<0)
L=max(L,-ans);//取最小下界
}
printf("%lld",H-L+1>0?H-L+1:0);//特判情况
return 0;
E(CodeForces 982C)
题目大意:给出一棵树,如果去掉一条边可以分成两个有偶数个点的连通块(子树)就可以去掉这条边,问最多能去掉几条边
思路:当树的节点个数为奇数个显然没有答案,当树的节点数为偶数的时候,以顶点1为父节点开始dfs计算子树的顶点个数,如果子树+1(该点)为偶数个顶点,代表去掉该点与其父节点的边必然能产生两个有偶数个点的连通块,去掉该边,计数,标记该子树,最后答案需-1,因为整棵树顶点为偶数个(不切也被计算了)
在本题中可以以任意一个点开始搜索,因为是一个连通图,并且没有硬性规定谁是树根或叶子节点
代码
#include <bits/stdc++.h>
using namespace std;
int n,u,v,cnt;
bool visit[121212];
vector<int>Tree[121212];
int DFS(int x) {
int ans=0;
visit[x]=1;
for(int i=0;i<Tree[x].size();i++)
if(!visit[Tree[x][i]])
ans+=DFS(Tree[x][i]);
if((ans+1)%2==0)//该点与其子树的和为偶数
cnt++;
return ans+1;
}
int main() {
scanf("%d",&n);
for(int i=0;i<n-1;i++) {
scanf("%d%d",&u,&v);
Tree[u].push_back(v);
Tree[v].push_back(u);//存树
}
if(n&1)
printf("-1");
else {
DFS(1);
printf("%d",cnt-1);
}
return 0;
}
F(CodeForces 1000C)
题目大意:给出n条线段以及每条线段的起始点和终止点,问被k条线段覆盖的点的个数,k从1到n
思路:由于数据量过大,因此开一个数组进行差分累和不现实,只能采用离散化的方法进行差分,具体看代码
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,data[212121],last,ans;//ans代表差分中的累和变量
map<ll,ll>M;
map<ll,ll>::iterator p;
int main() {
scanf("%lld",&n);
for(int i=0; i<n; i++) {
ll l,r;
scanf("%lld%lld",&l,&r);
M[l]++,M[r+1]--;//差分思想
}
for(p=M.begin(); p!=M.end(); p++) {//遍历区间
if(p==M.begin()) {
last=p->first;//区间起点
ans+=p->second;//起点线段覆盖数
continue;
}
data[ans]+=p->first-last;//记录覆盖数对应点数
last=p->first;
ans+=p->second;//控制覆盖数的大小
}
for(int i=1; i<=n; i++)
cout <<data[i]<<(i==n?"\n":" ");
return 0;
}