AtCoder ABC 277 A-E

链接

A - ^{-1}

给定一个数组和一个数 k k k ,求 k k k 在数组中出现的下标。

不讲。

#include
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
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
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 m10)即可。

一开始的莫名 W A WA WA 5 5 5 个点且用 m a p map map (一开始没想到排序)做的代码:

#include
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
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
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 几个点。

你可能感兴趣的:(算法,图论,c++)