本场比赛因为出题人对难度判断的失误,导致最终只有50人过三道题。
有一部分原因在于题面,C题的难度其实是低于H题的,只要根据所给代码的注释往前去找点积和激活函数的描述就好了,但是因为题面藏的过深,导致过题很少。
还有一部分原因在于简单题过于靠后,很多人还没有跟榜的习惯,导致被前面的题目劝退。
不多说了,我自觉上烧烤架Orz
比赛链接:http://acm.zzuli.edu.cn/contest.php?cid=1853
题库编号:2980 ~ 2989
A题:
打表,跑一遍所有的祝福值,用数组位置标志,方便以后查询。
然后读入形容词和名次表,排序之后枚举找到长度大于k的一组名称即为答案。
#include<bits/stdc++.h>
using namespace std;
const int mod = 114514;
int quick_POW(int x, int y) //快速幂
{
if(y==0)
return 1;
int tmp = quick_POW(x, y/2);
if(y%2==1)
return (long long)tmp*tmp%mod*x%mod;
return (long long)tmp*tmp%mod;
}
int homoit(int x) //祝福函数
{
return quick_POW(x, x+quick_POW(x, x+mod));
}
int homos[mod*2];
void gethomos() //打表,记录下每个祝福值
{
for(int i=0;i<mod;i++)
homos[homoit(i)] = true;
}
int main()
{
gethomos();
int n, m, k;
cin>>n>>m>>k;
string s;
vector<string> v1, v2;
// 将长度是祝福值的单词加入v1,v2
for(int i=1;i<=n;i++) {
cin>>s;
if(homos[s.size()])
v1.push_back(s);
}
for(int i=1;i<=m;i++) {
cin>>s;
if(homos[s.size()])
v2.push_back(s);
}
sort(v1.begin(), v1.end());
sort(v2.begin(), v2.end());
//枚举v1,v2中的单词,因为已经排序,找到的第一对长度大于k的单词即为答案
for(string s1:v1)
for(string s2:v2) {
if(s1.size()+s2.size()>k) {
cout<<s1<<' '<<s2<<endl;
return 0;
}
}
cout<<"None"<<endl;
}
B题
暴力搜索,搜索每种重量的分配可能,然后记录下最大值
#include<bits/stdc++.h>
using namespace std;
const int N = 20;
double ans;
double sum[N], power[N], a[N];
int n, m;
void dfs(int idx) //搜索,搜索到第几位用idx表示
{
if(idx>n) {// 到达递归出口,所有货物都已经被分配给某一个人,开始计算时间
double maxtmp= 0;
for(int i=1;i<=4;i++)
// 计算四人中要花费的最大时间
maxtmp = max(maxtmp, sum[i]*1.0/power[i]);
// 更新答案
ans = min(ans, maxtmp);
return ;
}
for(int i=1;i<=4;i++) {
sum[i]+=a[idx]; // 给第i个人背上编号为idx的哥布林
dfs(idx+1);// 在第i个人背上编号为idx的哥布林的条件下,搜索后续哥布林的背法
sum[i]-=a[idx];// 第i个人卸下编号为idx的哥布林,在下轮循环中装给下一个人
}
}
int main() {
ans = 1e18;
for(int i=1;i<=4;i++)
cin>>power[i];
cin>>n;
int sum = 0;
for(int i=1;i<=n;i++) {
cin>>a[i];
sum += a[i];
}
dfs(1);
printf("%.2f", ans);
}
C题
代码补全题,只要看明白了点积的定义,以及激活函数的描述就很好过。
程序实际上模拟的是深度神经网络的输出过程。
#include<bits/stdc++.h>
using namespace std;
/*
vector 向量,或者说动态数组的使用方法:
定义一个元素类型为double的动态数组:
vector<double> vec;
向动态数组中加入1到n:
for(int i=1;i<=n;i++)
vec.push_back(i);
获得动态数组的长度:
int len = vec.size();
遍历并输出动态数组:
for(int i=0;i<len;i++)
cout<<vec[i]<<' ';
*/
void read_vector(vector<double> &vec, int len)
{
double x;
for(int i=1;i<=len;i++) {
cin>>x;
vec.push_back(x);
}
}
void read(vector<vector<double>> &in_paras, int len_in, int len_out)
{
for(int i=1;i<=len_out;i++) {
vector<double> in_para;
read_vector(in_para, len_in);
in_paras.push_back(in_para);
}
}
string type;
const double e = 2.718;
double activate(double x)
{
//代码补全部分
if(type[0] == 'R')
return max(0.0, x);
else if(type[0] == 'S') {
return 1.0 / (1.0 + pow(e, -x));
}
return x;
}
double dot(vector<double> &in, vector<double> &in_para)
{
//代码补全部分
double ans = 0;
int len = in.size();
for(int i=0;i<len;i++) ans += in[i] * in_para[i];
return ans;
}
vector<double> get(vector<double> &in, vector<vector<double>> &in_paras, vector<double> &bias)
{
vector<double> outs;
int len_out = in_paras.size();
for(int i=0;i<len_out;i++) {
double out = activate(dot(in, in_paras[i])+bias[i]);
outs.push_back(out);
}
return outs;
}
double solve()
{
int n, m, k;
cin>>n>>m>>k;
cin>>type;
vector<double> in;
read_vector(in, k);
vector<vector<double>> in_paras[m+5];
read(in_paras[1], k, n);
for(int i=2;i<=m;i++)
read(in_paras[i], n, n);
read(in_paras[m+1], n, 1);
vector<double> bias[m+5];
for(int i=1;i<=m;i++) read_vector(bias[i], n);
read_vector(bias[m+1], 1);
for(int i=1;i<=m+1;i++) {
in = get(in, in_paras[i], bias[i]);
}
return in[0];
}
int main() {
vector<int> ans;
double x = solve();
double y = solve();
double z = solve();
if(y>x) swap(x, y);
if(z>y) swap(z, y);
if(y>x) swap(x, y);
printf("%.2f %.2f %.2f\n", x, y, z);
}
D题
将每个哥布林的魔力和运来的时刻记录在一个pair里,然后排序,每次循环数组时,如果当前哥布林的魔力大于等于上次的魔力,且时间小于等于当前的时间,就可以投入魔法阵,更新时间并标记一下哪个哥布林已经被投入魔法阵。循环多次即可得到结果。
ps:出题人建议把出题人投入魔法阵烤了
#include<bits/stdc++.h>
using namespace std;
int main() {
int n, m, x, y;
cin>>n>>m;
vector<pair<int, int> > a;
for(int i=1;i<=n;i++) {
cin>>x;
for(int j=1;j<=m;j++) {
cin>>y;
// 将魔力值作为第一键值,时间作为第二键值排序
a.push_back(pair<int, int>(y,x));
}
}
sort(a.begin(),a.end());
int cnt = 0, last = 0, t = 0, fir = 1;
vector<int> used(n*m+5,0);
while(cnt<n*m) {
int op = 0;
for(int i=0;i<a.size();i++) {
pair<int, int> x = a[i];
if(used[i]) continue;
// 如果符合条件,则投入法阵,更新used数组,更新时间t,更新当前魔力值last
if(x.first>=last&&x.second<=t) {
last = x.first;
t += x.first;
op = 1;
used[i] = 1;
if(fir) fir = 0;
else cout<<' ';
cout<<x.first;
cnt++;
}
}
if(!op) {
if(last)
last = 0;
else
t++;
}
}
cout<<endl;
}
E题
枚举题,枚举每两个队列,尝试交换,然后记录下最大值。
#include<bits/stdc++.h>
using namespace std;
struct node {
int a[20], n;
void input(int len) {
n = len;
for(int i=1;i<=n;i++)
cin>>a[i];
}
int get_max() {// 得到当前队列的队伍最高者
int ans = 0;
for(int i=1;i<=n;i++)
ans = max(ans, a[i]);
return ans;
}
int can_be_seen(int h) {// 得到对于前面最高的人是h的情况下,该队列有多少人能被看见
int cnt = 0;
for(int i=1;i<=n;i++)
if(a[i]>h) {
h = a[i];
cnt++;
}
return cnt;
}
};
node a[55][55];
int n, m;
int get_ans()
{
int cnt = 0;
for(int j=1;j<=m;j++) {
int h = 0;
for(int i=1;i<=m;i++) {
cnt += a[i][j].can_be_seen(h);
h = max(h, a[i][j].get_max());// 更新下当前列的最大值
}
}
return cnt;
}
int main()
{
cin>>n>>m;
int ans = 0;
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
a[i][j].input(n);
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
for(int ii=1;ii<=m;ii++)
for(int jj=1;jj<=m;jj++) {
swap(a[i][j], a[ii][jj]);// 交换任意两个队列
ans = max(ans, get_ans());
swap(a[i][j], a[ii][jj]);// 换回来
}
cout<<ans<<endl;
}
F题
这道题这里给出的标称是二分+搜索的写法,每次二分出一个限制的时间,然后dfs搜索能否在给定的时间内跑到房间n,由于从房间1到房间n至少存在一个小于114514的解,而且n数量很小,所以搜索是可解的。
这题同样可以用动态规划去写,用t,u分别代表当前时刻和当前位置,dp[t][u] = true表示t,u状态存在。
转移公式:if(dp[t][u]) dp[t+cost(u+v+t)][v] = true
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int n,m,firT;
const int mod = 114514;
int b[N*10];
long long poww(int x,int y)
{
if(y == 0) return 1;
long long tmp = poww(x, y>>1);
if(y&1) return tmp*tmp*x%mod;
return tmp*tmp%mod;
}
int homoit(int x)
{
return poww(x, x+poww(x, x+114514));
}
bool dfs(int u, int time, int nowt=0)// 限制时间的搜索
{
if(time<0)
return false;
if(u == n)
return true;
for(int v=n;v>=1;v--) {
int dis = homoit(nowt+u+v) + 1;
if(dfs(v, time-dis, nowt+dis))
return true;
}
if(dfs(u, time-1, nowt+1))
return true;
return false;
}
int main()
{
cin>>n>>firT;
int l = 0, r = 114514;
int ans = 0;
// 二分答案
while(l<=r) {
int mid = (l+r) >> 1;
if(dfs(1, mid, firT)) ans = mid, r = mid - 1;// 搜索在mid时间内能否搜索到可行解
else l = mid + 1;
}
cout<<ans<<endl;
}
G题
基本上算是一道模拟题,将时刻表打出来后,跟着伪代码把代码复现出来就好了,并不需要真正读懂伪代码。
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5;
int n,m,x,y,z,w,k,t;
char ch;
const int mod = 114514;
int a[N];
const int MAX = 1e18 + 7;
int hidecode;
long long poww(int x, int y)
{
if(y==0) return 1;
long long tmp = poww(x, y>>1);
if(y&1) return tmp*tmp%mod*x%mod;
return tmp*tmp%mod;
}
int homoit(int x) {
return poww(x, x+poww(x, x+114514));
}
// 实际上就是把now(),receive(),call("success")替换掉就可以了
void dec(int now, int t_end)
{
int u = a[now], fakeu;
for(int i=1;i<=100;i++) {
if(homoit(i+now+hidecode)==homoit(u+now+hidecode)) {
fakeu = i;
break;
}
}
now += fakeu;// 更新循环所费时间
if(now>t_end) {
cout<<"None"<<endl;
return;
}
if(homoit(now+fakeu+hidecode)==homoit(now+a[now]+hidecode)) {
cout<<now<<endl;// call("success")
return ;
}
dec(now, t_end);
}
int main() {
cin>>n>>hidecode;
for(int i=0;i<=10000;i++) a[i] = 0;
for(int i=1;i<=n;i++) {
cin>>t;
cin>>a[t];
}
for(int i=1;i<=10000;i++)
if(!a[i]) a[i] = a[i-1]; // 打印时刻表,把每个时刻在哪个位置都存起来
cin>>t;
dec(0, t);
}
H题
贪心题,从后往前贪心的选择让公主的血量最大,同时保证不大于后面一位的操作。
#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,z,w,k,t;
const int mod = 114514;
int a[mod],b[mod];
int main() {
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
cin>>b[i];
a[n+1] = 1<<31;
long long sum = 0;
for(int i=n;i;i--) {// 从后往前,选择使公主血量最大的操作
int tmp = b[i];// 操作三一定合法
if(a[i]+1<=a[i+1])// 判断操作一是否合法
tmp = max(a[i]+1, tmp);
if(a[i]<=a[i+1])// 判断操作二是否合法
tmp = max(a[i], tmp);
a[i] = tmp;
sum += tmp + b[i];
}
cout<<sum<<endl;
}
I题
循环解密,考察了循环读入
#include<bits/stdc++.h>
using namespace std;
int main()
{
char s[200];
scanf("%s", s);
int len = strlen(s);
int cnt = 0, x;
while(~scanf("%d", &x)) {
char ch = s[cnt%len] + x;
putchar(ch);
cnt++;
}
}