最后一舞--算法竞赛

星期三 更新 算法 1914 字 10 分钟阅读
最后一舞--算法竞赛
目录

牛客#

牛客暑假多校第一场#

A#

其实就是非常简单的模拟

#include<bits/stdc++.h>
using namespace std;
#define int long long
void slove(){
string s;
cin >> s;
bool t1 = 1;
if(s.size() != 8) t1 = 0;
string s1 = "aeiou";
for(int i = 0; i < 8; i++){
if(i % 2){
if(s1.find(s[i]) == string::npos){
t1 = 0;
}
}else{
if(s1.find(s[i]) != string::npos){
t1 = 0;
}
}
}
if(!t1){
cout << "Well-Being" << endl;
}else{
cout << "Suspected Virus" << endl;
}
}
signed main(){
int t = 1;
cin >> t;
while(t--){
slove();
}
}

C#

这道题我认为很有意思,其实就是带权并查集 + 连通块的问题: 有一个 n×mn\times m 的网格,鱼会不断加入。 鱼只能在上下左右移动:

  • 遇到大小 ≤\le 自己的鱼,可以吃掉,自己大小 +1+1
  • 遇到更大的鱼,不能通过
  • 障碍物也不能通过 题目有两种操作:
  1. 加入一条新鱼,问它最多能吃多少条鱼
  2. 对某条已有鱼,允许一开始给它增加体型,问为了吃完整个当前连通块,最少要增加多少

解决思路:

  1. 首先 操作1 其实就是维护一个连通块的size,但是这里要注意一个问题,就是你左边的方块可能和右边的方块是联通的
  2. 对于操作二,我们要找到一个关键的性质: s≥last−size[s]+1s \ge last - size[s] + 1, 如果我要往上吃,应该满足的性质,所以如果我要吃完连通块,就相当于 max(∑now...root(last−size+1))max(\sum_{now...root}(last - size + 1)),所以我们其实可以维护每一个 节点 到 根节点的 dp(吃完需要的最小体积),放到操作二处理,其实如何维护这个 dp,主要就是更新根节点的时候维护每一个连通块向上吃的最小体积,dp 向上传播的过程在find(并查集中实现就可以了)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dx[4] = {1,0,-1,0};
int dy[4] = {0,1,0,-1};
void slove(){
int n,m,q;
cin >> n >> m >> q;
vector<int> dp(n * m + 10 ,0);
vector<int> fa(n * m + 10, 0), sz(n * m + 10, 1), dj(n * m + 10, 0);
vector<bool> vis(n * m + 10, 0);
auto ID = [&](int x, int y) -> int {
return (x - 1) * m + y;
};
for(int i = 1; i <= n*m; i++){
fa[i] = i;
}
auto find = [&](int i) -> int {
vector<int> r;
int t1 = i;
while(t1 != fa[t1]){
r.push_back(t1);
t1 = fa[t1];
}
int root = t1;
int ed = r.size() - 1;
int cur = 0;
for(int k = ed; k >= 0; k--){
int id = r[k];
dp[id] = max(cur, dp[id]);
cur = dp[id];
fa[id] = root;
}
return root;
};
int l = 0;
for(int i = 1; i <= q; i++){
int op;
cin >> op;
if(op == 1){
int x, y, v;
cin >> x >> y >> v;
x ^= l; y ^= l;
dj[ID(x, y)] = v;
int root1 = ID(x, y);
vis[root1] = 1;
vector<int> ro;
for(int i = 0; i < 4; i++){
int x1 = x + dx[i], y1 = y + dy[i];
if(x1 < 1 || x1 > n || y1 < 1 || y1 > m) continue;
int id1 = ID(x1,y1);
if(!vis[id1]) continue;
int r = find(id1);
bool flag = 1;
for(auto tt : ro){
if(tt == r) flag = 0;
}
if(flag) ro.push_back(r);
}
for(auto x : ro){
dp[x] = max(0ll, v - sz[x] + 1);
fa[x] = root1;
sz[root1] += sz[x];
}
l = sz[root1] - 1;
cout << sz[root1] - 1 << endl;
}else if(op == 2){
int x, y;
cin >> x >> y;
x ^= l; y ^= l;
int root1 = ID(x, y);
find(root1);
l = max(0ll, dp[root1] - dj[root1]);
cout << max(0ll, dp[root1] - dj[root1]) << endl;
}
}
}
signed main(){
int t = 1;
// cin >> t;
while(t--){
slove();
}
}

E#

非常简单的一道题目,看规律都能看出来

#include<bits/stdc++.h>
using namespace std;
#define int long long
void slove(){
int n;
cin >> n;
vector<int> a(n + 1, 0);
for(int i = 1; i <= n; i++){
cin >> a[i];
}
int ans = 0;
for(int i = 1; i <= n; i++){
ans += (a[i] * i) - (a[i] * (n - i + 1));
}
cout << ans << endl;
}
signed main(){
int t = 1;
// cin >> t;
while(t--){
slove();
}
}

F#

解法:作为构造题,我们的想法不能过于复杂,从 公式中

Pj−PiP_j - P_i

的形式,为了保证 这个 f(P) 不变,所以根据这种形式,我们能不能从循环的形式去刻画这个问题,所以我们要去验证这个 循环是不是 f(P) 是否不变

#include<bits/stdc++.h>
using namespace std;
#define int long long
void slove(){
int n, k, x;
cin >> n >> k >> x;
vector<int> a(n, 0);
for(int i = 0; i < n; i++){
cin >> a[i];
}
int pos = 0;
for(int i = 0; i < n; i++){
if(a[i] == x){
pos = i;
break;
}
}
int t1 = (k - pos + n) % n;
vector<int> ans(n);
for(int i = 0; i < n; i++){
ans[(i + t1) % n] = a[i];
}
for(auto &x : ans) cout << x << " ";
cout << endl;
}
signed main(){
int t = 1;
// cin >> t;
while(t--){
slove();
}
}

G#

考虑这个 误差 θ≤0.01\theta \le 0.01 去考虑,发现计算,就是 节点直接距离看成1,可以用一个正方向包裹 100 个以上,所以我们在高度为一的上下两个平面去构建两个正方形就可以

#include<bits/stdc++.h>
using namespace std;
#define int long long
void slove(){
// cout << sqrt(1.01 * 1.01-1) / 0.0101 << endl;
int m;
cin >> m;
int t1 = m;
cout << 2 * m << endl;
for(int i = 0; i < 10; i++){
for(int j = 0; j < 10; j++){
cout << (i) * 0.0101 << " " << j * 0.0101 << " " << 0 << endl;
t1 --;
if(t1 == 0) break;
}
if(t1 == 0) break;
}
for(int i = 0; i < 10; i++){
for(int j = 0; j < 10; j++){
cout << (i) * 0.0101 << " " << j * 0.0101 << " " << 1 << endl;
m --;
if(m == 0) break;
}
if(m == 0) break;
}
// cout << "------------" << endl;;
}
signed main(){
int t = 1;
cin >> t;
while(t--){
slove();
}
}

H#

图片说明 这道题目如果赛时的话只能通过打表的形式去找规律了,不然很难想到 只要模拟前 100 轮,后面每轮的变化量差不多,其实就是 打表观察差分是否收敛的现象,然后再去模拟这个过程:

  • 首先当前步骤是 依靠后面步骤的最高收益,所以可以采用 DP,从后面的步骤开始往前面的步骤模拟,然后去模拟 A,B 的出牌选择 就可以了
#include<bits/stdc++.h>
using namespace std;
#define int long long
vector<vector<double>> dp(101, vector<double>(101, 0.00));
map<array<int,3>,int> id;
vector<array<int,3>> hand;
void slove(){
int k;
cin >> k;
string s1,s2;
cin >> s1 >> s2;
array<int,3> t1 = {0,0,0};
array<int,3> t2 = {0,0,0};
for(int i = 0; i < 3; i++){
if(s1[i] == 'R') t1[0] ++;
else if(s1[i] == 'S') t1[1] ++;
else if(s1[i] == 'P') t1[2] ++;
}
for(int i = 0; i < 3; i++){
if(s2[i] == 'R') t2[0] ++;
else if(s2[i] == 'S') t2[1] ++;
else if(s2[i] == 'P') t2[2] ++;
}
int ID1 = id[t1];
int ID2 = id[t2];
int state = ID1 * 10 + ID2;
cout << fixed << setprecision(10);
// cout << dp[k][state] << endl;
double ans = 0;
if(k <= 100){
cout << dp[k][state] << endl;
}else{
double d = dp[100][state] - dp[99][state];
cout << dp[100][state] + (k - 100) * d << endl;
}
}
void init(){
int id1 = 0;
for(int i = 0; i <= 3; i++){
for(int j = 0; j + i <= 3; j++){
int k = 3 - i - j;
array<int,3> temp = {i, j, k};
id[temp] = id1 ++;
hand.push_back(temp);
}
}
auto score = [&](int x1, int y1) -> double {
if((x1 == 0 && y1 == 1) || (x1 == 1 && y1 == 2) || (x1 == 2 && y1 == 0)){
return 3;
}else if((x1 == y1)){
return 1;
}else return 0;
};
for(int T = 1; T <= 100; T++){
// A 牌的种类
for(int i = 0; i < 10; i++){
// B 牌的种类
for(int j = 0; j < 10; j++){
int state = i * 10 + j;
// A 打哪张牌
double best = 0;
for(int i1 = 0; i1 < 3; i1++){
if(hand[i][i1] == 0) continue;
// B 打哪张牌
double worst = (double)1e9;
for(int j1 = 0; j1 < 3; j1++){
auto tt2 = hand[j];
auto tt1 = hand[i];
if(tt2[j1] == 0) continue;
tt2[j1] --;
tt1[i1] --;
double cur = score(i1, j1);
// 补牌环节
double s1 = 0;
for(int add1 = 0; add1 < 3; add1 ++){
for(int add2 = 0; add2 < 3; add2++){
auto temp1 = tt1;
auto temp2 = tt2;
temp1[add1] ++;
temp2[add2] ++;
int p1 = id[temp1];
int p2 = id[temp2];
int ID = p1 * 10 + p2;
s1 += dp[T-1][ID];
}
}
cur += (s1 / (9.0));
worst = min(worst, cur);
}
best = max(best, worst);
}
dp[T][state] = best;
}
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
init();
int t = 1;
cin >> t;
while(t--){
slove();
}
}

牛客暑假多校第二场#

B#

杭电 HDU#

HDU 多校第一场#

评论

欢迎留下你的想法,友善交流。