Codeforces Round #599 (Div. 2)
A:Maximum Square
题意:
给出n个柱子高度,问柱子可以构成的最大正方形是多大。(柱子只能竖着放)
解题思路:
将柱子排序,从后往前遍历,模拟正方形变大时所需要的最小柱子是多大就行了。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int N=1e3+10;
int T,n,m,k,ans;
int a[N];
int main(){
cin>>T;
while(T--){
cin>>n;
ans=0;
for(int i=0;i<n;i++)cin>>a[i];
sort(a,a+n);
for(int i=n-1;i>=0;i--){
if(a[i]>=ans+1)ans++;
else break;
}
cout<<ans<<endl;
}
return 0;
}
B1:Character Swap (Easy Version)
题意:
给出两个长度相等的字符串,你可以进行一次操作,该操作可以将两个字符串各一个字符交换,问是否可以进行交换后两个字符串相等。
解题思路:
若能相等,那么肯定仅存在两处字符不相等的地方,所以可以找到两个字符不相等的位置进行交换,最后判断字符串是否相等即可。
代码如下:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e3+10;
int T,n,m,k,ans;
int a[27],b[27];
string s1,s2;
int main(){
cin>>T;
while(T--){
cin>>n>>s1>>s2;
for(int i=0;i<n;i++){//找到两个字符不相等得位置并退出
if(s1[i]!=s2[i]){
for(int j=i+1;j<n;j++){
if(s1[j]!=s2[j]){
swap(s1[i],s2[j]);
break;
}
}
break;
}
}
if(s1==s2)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}
B2:Character Swap (Hard Version)
题意:
与上一题不同,这道题可以进行2*n次操作,问最后两个字符串能否相等,可以输出操作方案。
解题思路:
考虑贪心,假设我们已经考虑到了i位置,[0,i)区间的都已经相同了。
如果s1[i]!=s2[i]的情况,我们考虑首先交换s1[i]和s2[j],即能否在t里面找到和t[i]相同的;如果没有,我们再从s里面去找即可。假设s1[j]=s2[i],那么我们交换s1[j]和s2[n-1],再交换s1[i]和s2[n-1],只需要两次操作就可以使得s1[i]变成s2[i]了,那么我们这样最多操作2n次,就可以使得s1=s2了。
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+10;
int T,m,k,n,t;
int ans[N][2];
string s1,s2;
void solve(){
cin>>n>>s1>>s2;
t=0;
for(int i=0;i<n;i++){
bool is=false;
if(s1[i]!=s2[i]){//先从s2中找
for(int j=i+1;j<n;j++){
if(s2[j]==s2[i]){
ans[t++][0]=i+1,ans[t-1][1]=j+1;
swap(s2[j],s1[i]);
is=true;break;
}
}
if(!is){//如果没找到从s1找
for(int j=i+1;j<n;j++){
if(s1[j]==s2[i]){
ans[t++][0]=j+1,ans[t-1][1]=n;
ans[t++][0]=i+1,ans[t-1][1]=n;
swap(s1[j],s2[n-1]);swap(s1[i],s2[n-1]);
is=true;break;
}
}
}
if(!is){//如果都没有找到,则不能相等输出no
cout<<"No"<<endl;return;
}
}
}
cout<<"Yes"<<endl<<t<<endl;
for(int i=0;i<t;i++)cout<<ans[i][0]<<" "<<ans[i][1]<<endl;
return ;
}
int main(){
cin>>T;
while(T--){
solve();
}
return 0;
}
C:Tile Painting
题意:
现在有长度为n个方格需要染色,现在假设i这个格子染色了,那么所有的j满足 |j-i|>1 且 n%|j-i| == 0的格子都需要是同一个颜色,问最多可以染多少种颜色。
解题思路:
首先若该数字位质数,则答案就是n,所有首先将答案赋为n;
然后考虑循环节。
我们枚举n的所有的因子 a[1],a[2],a[3]…a[x]。翻译过来就是我们每a[1]个,都得相同;每a[2]个都得相同;…;每a[x]个都得相同。
那么实际上这个东西的循环节就等于他们的最小公倍数。那么最多个颜色就是n/lcm,实际上就是gcd。因为gcd x lcm = n。
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+10;
int T,m,k;
int a[27],b[27];
string s1,s2;
ll n,ans;
ll gcd(ll x,ll y){return x%y==0?y:gcd(y,x%y);}
int main(){
cin>>n;
ans=n;
for(ll i=2;i*i<=n;i++){
if(n%i==0){//有因子就计算gcd
ans=gcd(ans,i);
ans=gcd(ans,n/i);
}
}
cout<<ans;
return 0;
}
D:0-1 MST
题意:
给出一张完全图,最开始每一条边都是权值都为0,再输入m条边,每一条边得权值都变为1,问最后该图得最小生成树权值最小是多少。
解题思路:
考虑边权为0的点,如果两个节点之间的边权为0,那么这两个点可以变成一个点,相当于一个连通块,那么最后这张图可以变成几个点,想要连成最小生成树就需要添加几条边权为1的边就行,所以这道题可以变成这张图一共有多少连通块,那么答案就是连通块的数量减1。所以我们需要维护两个set集合进行优化,一个集合是当前已经聚合成连痛块的点,和一堆还在外面没有讨论过的点。
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
int T,m,k,n,ans;
set<int>node[N];//记录每一个节点与之相连的边权为1节点是多少
set<int>f;//记录还未加入连通块的节点
int vis[N];//判断节点是否被访问
void dfs(int x){
vis[x]=1;
f.erase(x);//f已经考虑就不再考虑了
vector<int>now;
for(int i:f){
if(!node[x].count(i)){//找到与搜索点相连的边权为0的点加入到now中
now.push_back(i);
}
}
for(int i:now)f.erase(i);//删除与该点相连的点,相当于将这几个点缩成一个点,就是连通块
for(int i:now)dfs(i);//搜索其它相连的点
}
int main(){
cin.sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
node[x].insert(y);
node[y].insert(x);
}
for(int i=1;i<=n;i++)f.insert(i);//先将所有点都加入到非连通块中
for(int i=1;i<=n;i++){
if(!vis[i]){
ans++;
dfs(i);
}
}
cout<<ans-1;
return 0;
}