A - ^{-1}
给定一个数组和一个数 k k k ,求 k k k 在数组中出现的下标。
不讲。
#include<bits/stdc++.h>
using namespace std;
int n,k,a[110];
int main(){
cin>>n>>k;
for(int i=1;i<=n;++i){
cin>>a[i];
if(a[i]==k) cout<<i<<endl;
}
return 0;
}
B - Playing Cards Validation
给出一些字符串,看看每个字符串的构成字符是否是某些字符中的一个(具体是什么过于简单,看个题目就懂了,不讲)。
#include<bits/stdc++.h>
using namespace std;
string s[60];
int n;
bool vh[100010],f[100010][2];
int main(){
cin>>n;
f['H'][0]=f['D'][0]=f['C'][0]=f['S'][0]=1;
for(int i=2;i<=9;++i) f[i+'0'][1]=1;
f['A'][1]=f['J'][1]=f['Q'][1]=f['K'][1]=f['T'][1]=1;
for(int i=1;i<=n;++i) cin>>s[i];
for(int i=1;i<=n;++i){
for(int j=0;j<=1;++j){
if(!f[s[i][j]][j]||vh[s[i][0]*100+s[i][1]]){
cout<<"No"<<endl;
return 0;
}
}
vh[s[i][0]*100+s[i][1]]=1;
}
cout<<"Yes"<<endl;
return 0;
}
C - Ladder Takahashi
有一栋楼,其中有一些楼层之间有无向边以相互到达,问从一楼出发最多可以到达的最高层是多少。
m a p map map 离散 + + + 宽搜,较易实现,不讲。复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
#include<bits/stdc++.h>
using namespace std;
int n,pos=0,ans=1;
map<int,bool>m;
map<int,int>rk;
queue<int>q;
vector<int>w[400010];
int main(){
cin>>n;
m.clear(),rk.clear();
for(int i=1;i<=n;++i){
int x,y;
cin>>x>>y;
int nk=rk[x];
if(!nk) rk[x]=++pos,nk=pos;
w[nk].push_back(y);
swap(x,y);
nk=rk[x];
if(!nk) rk[x]=++pos,nk=pos;
w[nk].push_back(y);
}
q.push(1);
while(!q.empty()){
int now=q.front();
q.pop();
if(m[now]) continue;
m[now]=1;
ans=max(ans,now);
int nk=rk[now];
for(int i=0;i<w[nk].size();++i) q.push(w[nk][i]);
}
cout<<ans<<endl;
return 0;
}
D - Takahashi’s Solitaire
给定一组数和一个整数 m m m ,从任意一个数开始拿,每次拿与上一个数相等的数或是等于 ( ( ( 上一个数 + 1 +1 +1 ) ) ) m o d mod mod m m m 的数,知道拿不下去。问最多能拿到的数字总和(最后结果用全部数字和减去)。
容易想到:拿的话一定是先将和上一个数相同的数拿完,然后再去拿下一个。这样问题就转化成:将数字从小到大排列,然后首位相接,形成一个环。取一段连续的数字(即类似:当 m = 7 m=7 m=7 时 223445660 223445660 223445660 行,而 11223566 11223566 11223566 不行)直到取不下去,计算出总和,然后打擂台统计。当然,我们可以发现,如果取一个类似 00112233 00112233 00112233 的东西之后,发现没有 4 4 4 了,我们就可以从 5 5 5 枚举(如果从 1 1 1 开始枚举,由于没有 4 4 4 ,则它最多取到 112233 112233 112233 ,不可能更新最优解。同理 1 1 1 ~ 3 3 3 都不用枚举,而 4 4 4 不存在,即可从 5 5 5 开始)。这意思就是说,如果你已经选择了一段 [ l , r ] [l,r] [l,r] ,发现无法再取了,就直接从 r + 2 r+2 r+2 开始下一轮枚举即可。
这里注意一下环的判断( m − 1 → 0 m-1\to 0 m−1→0)即可。
一开始的莫名 W A WA WA 了 5 5 5 个点且用 m a p map map (一开始没想到排序)做的代码:
#include<bits/stdc++.h>
using namespace std;
long long n,m,a[400010],ans=1e18,als=0;
map<long long,long long>mp;
int main(){
cin>>n>>m;
for(long long i=1;i<=n;++i){
cin>>a[i];
als+=a[i];
mp[a[i]]++;
a[i+n]=a[i];
}
for(map<long long,long long>::iterator it=mp.begin();it!=mp.end();++it){
long long p=(*it).first,sum=0,tm=0;
while(mp[p]&&tm<n) sum+=mp[p]*p,p=(p+1)%m,tm++;
p=(p+m-1)%m;
ans=min(ans,als-sum);
if((*it).first>p) break;
while((*it).first<=p+1&&it!=mp.end()) ++it;
--it;
}
cout<<ans<<endl;
return 0;
}
后来晚上听讲解听到一句排序就瞬间会打了的 A C AC AC 代码:
#include<bits/stdc++.h>
using namespace std;
long long n,m,a[400010],als=0,ans=1e18;
int main(){
cin>>n>>m;
for(long long i=1;i<=n;++i){
cin>>a[i];
als+=a[i];
}
sort(a+1,a+n+1);
for(long long i=1;i<=n;++i) a[i+n]=a[i]+m;
long long i=1;
while(i<=n){
long long p=i+1,sum=a[i];
while(a[p]-a[p-1]<=1&&p-i<n) sum+=a[p]%m,++p;
i=p;
ans=min(als-sum,ans);
}
cout<<ans<<endl;
return 0;
}
E - Crystal Switches
当时没做,边看题目边听讲解写出来的。
有一个无向图,以及一些长度为 1 1 1 无向边。每条无向边分为打开和关闭两种状态,只有打开的无向边才可以通行。有一些点上有开关,开关可以使所有原来打开的边关上,所有原来关上的边打开。问:从 1 1 1 号点到 n n n 号点最少要走几步(如无法到达,输出 − 1 -1 −1 )。
我也没怎么想,就听了听Sandwich__的讲解,具体思路是:将原来的图看成两个,其中一个图保留原来所有无向边的开关状态,另一个图将所有边的开关状态取反。所有有开关的点可以到达自己在另一个图上的点(长度为 0 0 0 ),这样每一次走长度为 0 0 0 的边就相当于摁了一次开关,最后求能到达 n n n 号点在两个图上任意一个的最短路即可。这个算法非常巧妙,值得学习,为一些动态更改的图问题提供了一种想法。
最短路采用堆优 d i j k s t r a dijkstra dijkstra ,复杂度 O ( n log n ) O(n\log n) O(nlogn) 。
#include<bits/stdc++.h>
using namespace std;
struct nod{
int to,d;
};
int n,m,k,x,y,z,dis[400010];
vector<nod>w[400010];
struct cmp{
bool operator()(nod u,nod v){
return u.d>v.d;
}
};
priority_queue<nod,vector<nod>,cmp>q;
bool vh[400010];
void dijkstra(){
q.push(nod{1,0});
memset(dis,127,sizeof(dis));
dis[1]=0;
while(!q.empty()){
nod now=q.top();
q.pop();
if(vh[now.to]) continue;
vh[now.to]=1;
for(int i=0;i<w[now.to].size();++i){
int nt=w[now.to][i].to,nd=w[now.to][i].d;
if(dis[nt]>dis[now.to]+nd){
dis[nt]=dis[now.to]+nd;
q.push(nod{nt,dis[nt]});
}
}
}
if(min(dis[n],dis[n+n])>1e9) cout<<-1<<endl;
else cout<<min(dis[n],dis[n+n])<<endl;
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=m;++i){
cin>>x>>y>>z;
if(z) w[x].push_back(nod{y,1}),w[y].push_back(nod{x,1});
else w[x+n].push_back(nod{y+n,1}),w[y+n].push_back(nod{x+n,1});//x+n以表示另一个图上的自己
}
for(int j=1;j<=k;++j){
cin>>x;
w[x].push_back(nod{x+n,0});
w[x+n].push_back(nod{x,0});
}
dijkstra();
return 0;
}
总结:
想得充分了再打代码。
边界情况多想一想,防止
W
A
WA
WA 几个点。