A - Prime Ring Problem
原链接挂了,有个洛谷的链接:素数环 Prime Ring Problem - 洛谷
一开始想到的是全排列,但是复杂度是n!,不可行。所以改用dfs(因为dfs可加入剪枝)
#include <bits/stdc++.h>
using namespace std;
int t,a[20],n,p[40], vis[20];
void init(){ //打素数表(打到39就能停了)
p[2]=p[3]=p[5]=p[7]=p[11]=p[13]=p[17]=p[19]=p[23]=p[29]=p[31]=1;
a[1] = 1; vis[1] = 1;//第一个位置必须是1,那么1已经用过了
}
void dfs(int x){
if(x==n+1 && p[a[n]+a[1]]){
for(int i=1;i<n;i++) cout<<a[i]<<' ';
cout<<a[n]<<'\n';
}
if(x==n+1) return;
for(int i=2;i<=n;i++){
if(vis[i] || !p[i+a[x-1]]) continue;
a[x]=i; vis[i]=1;
dfs(x+1);
vis[i]=0;
}
}
int main(){
init();
while(cin>>n){
if(t) cout<<'\n';
printf("Case %d:\n",++t);
dfs(2);
}
return 0;
}
B - Catch That Cow
一开始想用贪心,但后来发现并不可行。本题要用bfs,需要注意几处剪枝:
- 起点坐标比终点大,只能后退,步数直接输出n-k
- 遍历过的地方避免重复遍历,加入vis数组进行剪枝
另外,还要注意走到的地方不能越界。
#include <iostream>
#include <queue>
using namespace std;
int n, k, cnt;
bool vis[1000005];
int main(){
cin>>n>>k;
if(k<n) {cout<<n-k<<endl; return 0;}
queue<int> q;
q.push(n);
while(q.size()){
int size = q.size();
while(size--){
int c = q.front(); q.pop();
//
if(vis[c]) continue;
if(0<c && c<100005) vis[c] = 1;
//
if(c==k) {cout<<cnt; return 0;}
else{
if(0<c-1 && c-1<100005) q.push(c-1);
if(0<c+1 && c+1<100005) q.push(c+1);
if(0<2*c && 2*c<100005) q.push(2*c);
}
}
cnt++;
}
}
C - Cinema
用map记录认识每种语言的科学家的人数,然后O(N)把所有电影扫一遍即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 200005;
int n, m, tmp; //科学家数量,电影数量
int a[maxn], b[maxn], max1, max2, id = 1; //注意id初始必须设为1,否则会出现结果是0的错误答案
map<int, int> mp;
int main(){
scanf("%d",&n);
for(int i=1; i<=n; i++) {scanf("%d", &tmp); mp[tmp]++;} //离散化记录
scanf("%d",&m);
for(int i=1; i<=m; i++) scanf("%d", &a[i]);
for(int i=1; i<=m; i++){
scanf("%d", &b[i]);
if(mp[a[i]] > max1 || (mp[a[i]] == max1 && mp[b[i]] > max2)){
id = i;
max1 = mp[a[i]];
max2 = mp[b[i]];
}
}
cout<<id;
}
G - Restorer Distance
设定的高度如果很高或者很低,需要的操作次数都很大,而高度在中间的时候则小一些,满足单峰性,用三分答案解决。注意可以预处理移动砖块的成本为 min(移动一块,加一块+减一块)。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e5+5;
int n, a, r, m, arr[maxn], lb=1e9, rb=-1e9;
int func(int x){
int low=0, up=0, res = 0;
for(int i=1; i<=n; i++){
if(arr[i] < x) low += (x-arr[i]);
else up += (arr[i]-x);
}
//1.移动
int move = min(up, low);
res += move*m;
//2.加或者减
res += (low-move)*a;
res += (up-move)*r;
return res;
}
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>a>>r>>m;
m=min(m,a+r);//预处理
for(int i=1; i<=n; i++){
cin>>arr[i];
lb = min(arr[i], lb); rb = max(arr[i], rb);
}
while(lb < rb){
int m1 = lb+(rb-lb)/3, m2 = rb-(rb-lb)/3;
if(func(m1) <= func(m2)) rb = m2-1;
else lb = m1+1;
}
cout<<func(lb);
}
J - Meteor Shower
看起来好像有点难,实际上就是一个bfs,用二维数组记录流星落下时间和安全位置即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 400;
const int dx[4]={1,-1,0,0}, dy[4]={0,0,1,-1};
bool vis[maxn][maxn]; //优化:遍历过的点不再遍历
int m, mp[maxn][maxn];
void bfs(){
if(mp[0][0]==0) {cout<<-1; return;}
if(mp[0][0]==-1) {cout<<0; return;}
int t = 0;
queue<pair<int,int> > q;
q.push(make_pair(0,0));
while(q.size()){
t++;
int size = q.size();
while(size--){
pair<int,int> P = q.front(); q.pop();
int x=P.first, y=P.second;
if(vis[x][y]) continue; //之前遍历过了,跳过
vis[x][y] = 1;
for(int i=0; i<4; i++){
int xi=x+dx[i], yi=y+dy[i];
if(xi<0 || yi<0) continue; //跳过越界格子
if(mp[xi][yi]==-1) {cout<<t; return;} //找到安全地方,结束
if(t>=mp[xi][yi]) continue; //被破坏了,跳过
q.push(make_pair(xi,yi));
}
}
}
cout<<-1;
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>m;
int x,y,t;
memset(mp,-1,sizeof(mp)); //init
for(int i=1; i<=m; i++){
cin>>x>>y>>t;
mp[x][y] = (mp[x][y]==-1? t : min(mp[x][y], t)); //注意取最小
// if(mp[x][y] == -1) mp[x][y] = t;
// else mp[x][y] = min(mp[x][y], t);
for(int i=0; i<4; i++){
int xi=x+dx[i], yi=y+dy[i];
if(xi<0 || yi<0) continue; //跳过越界格子
mp[xi][yi] = (mp[xi][yi]==-1? t : min(mp[xi][yi], t)); //注意取最小
// if(mp[xi][yi] == -1) mp[xi][yi] = t;
// else mp[xi][yi] = min(mp[xi][yi], t);
}
}
bfs();
}
O - K Best
二分+贪心,思路不好想到,参考了:K Best(最大化平均值(二分搜索))_Crazy-CSDN博客_kbest
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5+5;
const double eps = 1e-6;
struct node{int v,w,id;} a[maxn];
int n, k; double l, r, s;
bool cmp(const node& a, const node& b) {return a.v-s*a.w > b.v-s*b.w;}
bool check(double s){ //检查平均价值能否到达s
double sum = 0;
sort(a+1,a+n+1,cmp);
for(int i=1; i<=k; i++) sum += a[i].v-s*a[i].w;
return sum >= 0;
}
int main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>n>>k;
for(int i=1; i<=n; i++) {cin>>a[i].v>>a[i].w; a[i].id=i; r=max(r,(double)a[i].v/a[i].w);}
while(r-l>eps){
s = (l+r)/2;
if(check(s)) l=s;
else r=s;
}
for(int i=1; i<=k; i++) cout<<a[i].id<<' ';
}
P - Median
两重二分搜索,详见代码。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e6+5;
int n, m, a[maxn];
bool check(int d){
int cnt = 0;
for(int i=1; i<n; i++){
//计算差异值 <= d的数量,是upper_bound找到的位置和当前位置中间夹着的部分
cnt += upper_bound(a+i+1,a+n+1,a[i]+d)-(a+i)-1;
}
return cnt >= m; //数量足够,也可能过多
}
int main(){
while(scanf("%d",&n) != EOF){
m = (n*(n-1)/2+1)/2; //差异总数一半向上取整
for(int i=1; i<=n; i++) scanf("%d",a+i);
sort(a+1, a+n+1);
int l=0, r=a[n]-a[1], ans;
while(l<=r){
int mid = (l+r)>>1;
if(check(mid)) {ans=mid; r=mid-1;}
else l=mid+1;
}
printf("%d\n",ans);
}
}