首先一到四题签到题没有问题,只是打字比较慢。在比赛中只做出来了前四个签到题,痛心疾首,创建文件出了问题。
1.L1-5 yihan的新函数
本题需要注意以字符串输入数据如何转化为整形数据以及如何储存的问题。比赛时我将处理后的数据又放到了数组中,增加了运算量。
#include<bits/stdc++.h>
using namespace std;
int ans=0;
void solve(string a)
{
int sum=0;
if(a.size()%2==0) for(int i=0;i<a.size();i++){
if(i%2!=0)sum=sum*10;
else sum=(sum)*10+(a[i]-'0');
}
else for(int i=0;i<a.size();i++){
if(i%2!=0)sum=sum*10;
else sum=(sum)*10+(a[i]-'0');
// cout<<sum<<" ";
}
ans+=sum;
// cout<<ans<<" "<<a.size()<<endl;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++){
string a;
cin>>a;
solve(a);
}
cout<<ans;
return 0;
}
2.L1-6 二进制
同上一题,思路相同,比赛时未注意到最后一位的细节,导致出错。
#include<bits/stdc++.h>
using namespace std;
int ans=0;
int a[10000010] ,b[10000010],c[10000010];
int main()
{
string a1,b2;
cin>>a1>>b2;
for(int i=0;i<a1.size();i++){
a[a1.size()-i-1]=a1[i]-'0';
}
for(int i=0;i<b2.size();i++){
b[b2.size()-i-1]=b2[i]-'0';
}
for(int i=0;i<max(a1.size(),b2.size());i++)
{
c[i]+=a[i]+b[i];
if(c[i]>=2){
c[i+1]=(c[i])/2;
c[i]=(c[i])%2;
}
}
int k=max(a1.size(),b2.size());
if(c[k]==0) k-=1;
for(int i=k;i>=0;i--){
cout<<c[i];
}
return 0;
}
3.L1-7 大山中的学院
很简单的宽搜问题,比赛时因为前两个没写出来,所以没写这个题。
#include <bits/stdc++.h>
using namespace std;
int n,m,k;
const int N=2e5+1;
char A[N][N];
bool B[N][N];
int c[N][N];
int ans=0;
int sx=0,sy=0;
int dx[4]={1, 0, -1,0},dy[4]={0,1,0,-1};
void solve(int x,int y)
{
if(B[x][y])
{
int k1=0;
for(int i=0;i<4;i++)
{
int a=x+dx[i];
int b=y+dy[i];
if(a>=0&&a<n&&b>=0&&b<m)
{
if(A[a][b]=='*'){
k1+=c[a][b];
}
}
}
if(ans<k1) { ans = k1; sx=x;sy=y;}
}
}
int main()
{
cin>>n>>m>>k;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
cin>>A[i][j];
if(A[i][j]=='-') B[i][j]=true;
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(A[i][j]=='*') {
cin >> c[i][j];
}
}
}
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
solve(i,j);
cout<<sx<<" "<<sy<<endl<<ans;
return 0;
}
4.L1-8 堆积木
暴力解决问题,按照题目模拟即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+1;
#define PII pair<int,int>
int b[10001];
int kp=1;
void cake(int k,vector<PII>&a)
{
int w=0;
int minn=INT_MAX,maxn=INT_MIN;
for(int i=0;i<kp;i++)
{
if(k>=a[i].second) {
w++;
b[i]=INT_MAX;
}
else
b[i]=a[i].second-k;
minn=min(minn,b[i]);
}
int key=0;
if(w==kp) {
a[kp].first = 1;
a[kp].second = k;
kp++;
}
else
{
for(int i=0;i<kp;i++)
{
if(minn==b[i])
{
int st=a[i].first;
if(st>maxn){
maxn=st;
key=i;
}
}
}
a[key].first++;
a[key].second=k;
}
}
int main()
{
vector<PII>a;
int n;
cin>>n;
a.reserve(n);
for(int i=0;i<n;i++)
{
int k;
cin>>k;
if(i==0) {
a[0].first = 1;
a[0].second = k;
}
else
cake(k,a);
}
int maxn=INT_MIN;
for(int i=0;i<kp;i++)
{
if(a[i].first>maxn) maxn=a[i].first;
}
cout<<maxn<<" "<<kp;
}
5.L2-1 买!买!买!
看到题也不知道该怎么写,只能当作挑战题,边学算法边做题。
问题可以抽象为询问图内连通块的个数连通块中有环的个数。在每次dfs的时候初始化一个值$flag=1$ 代表当前遍历的连通块中是否有环,在dfs的过程中标记经过的灯塔,若发现要遍历的灯塔已经被标记过了,则讲$flag$ 赋值为0,若完成一个连通块的标记之后flag值任然为1,则可以认为这个联通块中没有环。直接dfs遍历每个连通块即可。
##dfs 写法
a.用vector容器模拟二维数组存放
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int vis[N];
int fg;
vector<vector<int>> adj;
void dfs(int u,int fa)
{
vis[u]=1;
for(auto to:adj[u]) if(to!=fa)
{
if(vis[to])
{
fg=1;
continue;
}
dfs(to,u);
}
}
void solve()
{
int n,m;cin>>n>>m;
adj.resize(n+1);
for(int i=1;i<=m;i++)
{
int u,v;cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
}
int res=0,ans=0;
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
fg=0;
res++;
dfs(i,0);
ans+=fg;
}
}
cout<<res<<'\n'<<ans<<'\n';
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t=1;
while(t--) solve();
}
b.用三个数组模拟链表储存
##并查集写法(暂作积累)
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m;
int f[N];
bool st[N];
int find(int k)
{
return f[k] == k ? k : f[k] = find(f[k]);
}
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++) f[i] = i;
while(m --)
{
int a, b;
cin >> a >> b;
a = find(a), b = find(b);
if(a != b)
{
f[a] = b;
st[b] |= st[a];
}
else st[a] = true;
}
int cnt1 = 0, cnt2 = 0;
for(int i = 1; i <= n; i ++)
{
if(f[i] == i)
{
cnt1 ++;
if(st[i]) cnt2 ++;
}
}
cout << cnt1 << ' ' << cnt2;
}
6.L2-2 洗牌&发牌
写了才知道,原来如此简单。按题暴力模拟就行。
#include<bits/stdc++.h>
using namespace std;
int n,k,m,p;
#define PII pair<int,char>
void solve(vector<PII>&a,vector<PII>&b)
{
for(int j=0;j<m;j++){
for(int i=2;i<=k;i+=2){
b[i].first=a[i/2].first;
b[i].second=a[i/2].second;
b[i-1].first=a[k/2+i/2].first;
b[i-1].second=a[k/2+i/2].second;
}
for(int i=1;i<=k;i++) {
a[i].first = b[i].first, a[i].second = b[i].second;
}
}
int q=0;
for(int i=1;i<=k&&q<4;i++)
{
if((i-1)%n+1==p){
if(a[i].first==1){
cout<<"A";
}
else if(a[i].first==11){
cout<<"J";
}
else if(a[i].first==12){
cout<<"Q";
}
else if(a[i].first==13){
cout<<"K";
}
else{
cout<<a[i].first;
}
cout<<a[i].second<<endl;
q++;
}
}
return ;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>k>>m>>p;
vector<PII>a,b;
a.reserve(k);
b.reserve(k);
for(int i=1;i<=k;i++){
cin>>a[i].first>>a[i].second;
}
if(n*3+p>k) cout<<"Error:cards not enough";
else solve(a,b);
return 0;
}
7.L2-3 Gwen的小剪刀(代码如下,暂未解决)
本题的核心在于找出可以使得图联通的边中 $W$ 值最大的边,这是一个经典的二分最小生成树问题,即每次二分边权,利用并查集判断大于 $W$ 的所有边能能否使得图连通 ,也可以通过对 $W$ 进行一次最大生成树,这颗最大生成树中 $W$ 最小的边即为 使得图连通的最小值最大的 $W$ 记为 $W_{min}$,当前最小值最大的边的权值为$W_{min}$,将所有 $W$ 值小于 $W_{min}$ 的边舍弃掉,对剩下的边进行一次对 $C$ 的最小生成树即可。最小生成树的权值总合即为答案。
a.二分写法
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int,int> ;
//#define int long long
#define endl '\n'
const int INF = 0x3f3f3f3f;
void solve()
{
cin>>n>>m;
vector<array<int,4>> adj;
int l=1e9,r=0;
for(int i=1;i<=m;i++)
{
int x,y,w,v;cin>>x>>y>>w>>v;
adj.push_back({x,y,w,v});
r=max(r,w);
l=min(l,w);
}
auto cmp = [&](array<int,4> a,array<int,4> b)
{
return a[2]<b[2];
};
sort(adj.begin(),adj.end(),cmp);
auto check = [&](int x)
{
vector<int> f(n+1);
function<int(int)> find = [&](int x)
{
return x==f[x]?x:f[x]=find(f[x]);
};
for(int i=1;i<=n;i++) f[i]=i;
int cnt=0;
for(auto [a,b,w,v]:adj)
{
if(w<x) continue;
a=find(a),b=find(b);
if(a!=b)
{
f[b]=f[a];
cnt++;
}
}
return cnt==n-1;
};
while(l<=r)
{
int mid=l+r>>1;
if(check(mid)) l=mid+1;
else r=mid-1;
}
vector<int> f(n+1);
function<int(int)> find = [&](int x)
{
return x==f[x]?x:f[x]=find(f[x]);
};
ll ans=0;
for(int i=1;i<=n;i++) f[i]=i;
auto cmp1 = [&](array<int,4> a,array<int,4> b)
{
return a[3]<b[3];
};
sort(adj.begin(),adj.end(),cmp1);
for(auto [a,b,w,v]:adj)
{
if(w<r) continue;
a=find(a),b=find(b);
if(a!=b)
{
f[b]=f[a];
ans+=v;
}
}
cout<<r<<endl<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t=1;
while(t--) solve();
}
b.最大生成树和最小生成树
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e5+10;
const int M = 2e5+10;
struct node{
int x,y,w,c;
}e[M];
int tot;
int head[N];
int n,m;
int f[N];
int find(int x){
if(x==f[x])return x;
return f[x]=find(f[x]);
}
bool cmp(node a, node b){
return a.w > b.w;
}
ll result = 0;
ll W;
void kruskal(){
sort(e,e+tot,cmp);
for(int i=0;i<tot;i++){
int u = find(e[i].x);
int v = find(e[i].y);
if(u!=v){
f[u]=v;
W=e[i].w;
}
}
}
bool cmp1(node a, node b){
return a.c < b.c;
}
void kruskal1(){
sort(e,e+tot,cmp1);
for(int i=0;i<tot;i++){
if(e[i].w<W) continue;
int u = find(e[i].x);
int v = find(e[i].y);
if(u!=v){
f[u]=v;
result+=e[i].c;
}
}
}
void solve(){
cin >> n >> m;
ll ans2 = 0;
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++){
int x,y,z,c;
cin >> x >> y >> z >> c;
e[tot++]={x,y,z,c};
}
kruskal();
//由于kruskcal算法的特性,最后一次加入最大生成树的边一定是这颗生成树中W值最小的
for(int i=1;i<=n;i++)f[i]=i;
kruskal1();
cout << W << endl;
cout << result << endl;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t=1;
while (t--) solve();
}
8.L2-4 超时空之恋(未解决)
多层图最短路裸题,一开始用链式前向星加入同层道路,后建立不同层相同点之间的路径。最后用spfa或者dijkstra跑一遍最短路即可
细节:最后给出的地牢出现时间是随机的,需要对地牢出现的时间排序,因为下次出现地牢上一次就会消失