Codeforces Round #763 (Div. 2)
A - Robot Cleaner
思维
题意:n*m的地图,一个机器人初始在一个位置,一开始往右下方走,碰到墙壁就改变方向,类似于光的折射(原题有图),每次可以清理和机器人同行或同列的格子,问最少几步清理目标.
思路:数据范围很小,模拟一下就可以了,类似于dfs的函数,记录好方向坐标。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long int ll;
typedef pair<int, int> pii;
const int N = 105;
bool flag = false;
int ans = 0;
int n, m, sx, sy, ex, ey;//地图尺寸,起点,终点
//x,y表示当前走到哪里,now表示步数,xx,yy表示上一步的方向坐标
void dfs(int x, int y,int now,int xx,int yy) {
if (x == ex || y == ey|| flag) {
ans = now;
flag = true;
return;
}
int tx = xx, ty = yy;
if (x == n) tx = -1; if (x == 1) tx = 1;
if (y == m) ty = -1; if (y == 1) ty = 1;
dfs(x + tx, y + ty, now + 1, tx, ty);
return;
}
void solve() {
cin >> n >> m >> sx >> sy >> ex >> ey;
ans = 0; flag = false;
dfs(sx, sy, 0, 1, 1);
cout << ans << endl;
}
int main() {
ios::sync_with_stdio(false);
int t; cin >> t;
while(t--)
solve();
return 0;
}
B - Game on Ranges
广搜
题意:一个1-n的集合,n个[l,r],每次在区间中选一个数删掉,问完全删完1-n的方法.
思路:数据范围很小,所以可以考虑广搜,开始加入区间内所有数入队,每个结点用set维护已经选了哪些数,如果已经被选中直接跳过,最后肯定会有一个可行解。
MLE了一次,sort区间长度从小到大后过了,其实一开始就想到了优化搜索树,没看到题目里说区间可以任意顺序,🤭。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<set>
#include<queue>
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long int ll;
typedef pair<int, int> pii;
const int N = 1e3 + 5;
typedef struct node {
set<int> s;
vector<int> p;
};
bool cmp(pii a, pii b) {
return abs(a.second - a.first) < abs(b.second - b.first);
}
void solve() {
int n;
cin >> n;
pii a[N];
mem(a, 0);
for (int i = 1; i <= n; i++) {
int l, r; cin >> l >> r;
a[i].first = l;
a[i].second = r;
}
sort(a + 1, a + 1 + n, cmp);
vector<int> ans;
queue<node> q;
//把长度最小的区间派生出的结点们入队
for (int i = a[1].first; i <= a[1].second;i++) {
node now;
now.p.push_back(i);
now.s.insert(i);
q.push(now);
}
while (!q.empty()) {
node now = q.front();
q.pop();
if (now.p.size() == n) {//得到答案
ans = now.p;
break;
}
int cnt = now.p.size() + 1;//cnt表示下一个区间编号,注意要+1
int l = a[cnt].first, r = a[cnt].second;
for (int i = l; i <= r; i++) {
if (now.s.count(i)) continue;//数已经出现过,跳过
node temp = now;
temp.s.insert(i);//更新
temp.p.push_back(i);
q.push(temp);
}
}
for (int i = 1; i <= n; i++) {
cout << a[i].first << " " << a[i].second << " " << ans[i - 1] << endl;
}
}
int main() {
ios::sync_with_stdio(false);
int t; cin >> t;
while (t--)
solve();
return 0;
}
C_Balanced Stone Heaps
二分
题意:n堆石子,从第三个开始,可以选择:1.拿出3*d份,给n-1分d份,给n-2分2d份,2.不操作。问这样操作后最小堆的最大值是多少
思路:看到最大化最小值,就能想到二分。如果从前往后二分,每一次还要考虑更新后的结果。所以我们从后往前二分,因为每堆石子只能被他后面的影响,并且分给前面的,所以设二分值k为答案,从后往前遍历,如果当前石头原始就小于k,表示在后面已经合法分完的情况下,当前堆还是小于想要的答案,所以直接return false。如果大于,那么可以直接分。
此时要注意,因为我们二分是从后往前处理,所以我们的当前石子堆的数量已经被后面的给了一部分,也就是大于原来的,我们能给的数量最大就是当前堆-k。但是题目要求是从前往后处理,你吧这一堆分给前面两个,此时这一堆还未被后面的给与,所以分的最大值就是原始堆的数量。**res=min(b[i]-mid,a[i])**就是这样来的。
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
typedef long long int ll;
const int N = 2e5+5;
int n;
ll a[N],b[N];
bool check(ll mid){
for(int i=1;i<=n;i++)
b[i]=a[i];
for(int i=n;i>=3;i--){
if(b[i]<mid) return false;
ll res=(b[i]-mid)/3;
res=min(res,a[i]/3);//最大不能超过1/3
b[i]-=res*3;
b[i-1]+=res;
b[i-2]+=res*2;
}
return b[1]>=mid&&b[2]>=mid;
}
//每一次挪的时候 还没有被后面石头增加
//所以 判断一下 res = min(b[i]-mid,a[i])/3
void solve(){
mem(a,0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll l=0,r=INT_MAX;
while(l<=r){
ll mid = (l+r+1)/2;
if(check(mid)) l=mid+1;
else r=mid-1;
}
cout<<l-1<<endl;
}
int main(){
ios::sync_with_stdio(false);
int t;cin>>t;
while(t--)
solve();
return 0;
}