文章目录
填空题链接
试题 F: 完全二叉树的权值(用队列来模拟,注意数据范围爆int,记得多调试多输出!)
#include <iostream>
#include <queue>
using namespace std;
int main()
{
int n;
cin >> n;
queue<int> q;
for(int i = 0;i<n;i++) {
int x;
cin >> x;
q.push(x);
}
int maxv = 0, res_level = 0;
int m = 1,level = 1;
while(q.size())
{
long long sum = 0;
int mt = m;
while(mt -- && q.size()) // 加多一个判断条件,可能q为空了
{
int t = q.front();
q.pop();
sum += t;
}
//cout << "level = " << level <<" sum = "<<sum << endl;
if(sum > maxv){
maxv = sum;
res_level = level;
}
m *= 2;
level ++; // 记得更新
}
cout << res_level << endl;
/*
8
1 6 5 4 3 2 1 100
*/
return 0;
}
试题 G: 外卖店优先级(模拟,对时间排序,相同订单合并处理就不用那么多特判,)
算法1:y总写法(将相同订单合并处理,妙!)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N = 100010;
typedef pair<int,int> PII;
int score[N],last[N],st[N];
PII order[N];
int main()
{
int n,m,t;
scanf("%d%d%d",&n,&m,&t);
for(int i=0;i<m;i++) scanf("%d%d",&order[i].x,&order[i].y);
sort(order,order+m);
for(int i=0;i<m;)
{
int j=i;
while(j<m && order[j] == order[i]) j++; // 将相同订单合并处理,妙!
int t=order[i].x, id=order[i].y,cnt=j-i;
i=j;
score[id] -= t - last[id] -1;
if(score[id] < 0) score[id] = 0;
if(score[id] <=3) st[id]=false;
score[id] += cnt * 2;
if(score[id] > 5) st[id]=true;
last[id]=t;
}
for(int i=1;i<=n;i++)
{
if(last[i]<t){
score[i] -= t - last[i];
if(score[i] <=3) st[i]=false;
}
}
int res=0;
for(int i=1;i<=n;i++) res+=st[i];
printf("%d\n",res);
return 0;
}
算法2:我的写法(疯狂纠错)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
typedef pair<int,int> PII;
int n,m,t;
PII order[N];
int last_time[N],priori[N]; // 上一次接单时间,优先级
bool st[N]; // 是否在优先队列中
int main()
{
cin >> n >> m >> t;
for(int i =0;i < m ;i ++)
{
int ts,id;
cin >> ts >> id;
order[i] = {ts,id};
}
sort(order,order+m);
for(int i = 0;i < m ;i ++ )
{
int ts = order[i].first, id = order[i].second;
// 存在相同时刻的订单,特判!
if(ts == last_time[id]);
else priori[id] = max(0,priori[id] - (ts - last_time[id] - 1));
if(priori[id] <= 3) st[id] = false; // 先减后加
priori[id] += 2;
if(priori[id] > 5) st[id] = true;
last_time[id] = ts;
}
// 最后还要算一下t时刻的衰减
for(int i = 1;i <= n;i ++)
{
priori[i] = max(0,priori[i] -(t - last_time[i] ));
if(priori[i] <= 3) st[i] = false; // 先减后加
}
int res = 0;
for(int i = 1;i <= n;i ++ )
res += st[i];
cout << res << endl;
//for(int i = 1;i<=n;i++) cout << priori[i]<<" ";
return 0;
}
试题 H: 修改数组(另类并查集 or 平衡树)
算法1:另类并查集(根元素表示第一个没有被用过的位置,时间复杂度O(N + M)
)
#include <iostream>
using namespace std;
const int N = 1e6 + 1e5;
int p[N];
int find(int x)
{
if(p[x]!=x) p[x] = find(p[x]);
return p[x];
}
int main()
{
int n;
cin >> n;
for(int i = 1;i <= N;i ++ ) p[i] = i; // 初始化并查集
for(int i = 1;i <= n;i ++ )
{
int x;
cin >> x;
x = find(x);
cout << x << " ";
p[x] = x + 1;
}
return 0;
}
算法2:平衡树(维护区间,没写,时间复杂度O(nlogn)
)
试题 I: 糖果(状态压缩DP,数据范围小,小于32位)
#include <iostream>
#include <cstring>
using namespace std;
int n,m,k;
int can[100][20], dp[1 << 20];
int main()
{
cin >> n >> m >> k;
for(int i = 0;i < n;i ++ )
for(int j = 0;j < k ;j ++ )
{
cin >> can[i][j];
can[i][j] --; // 下标从0开始
}
memset(dp,-1,sizeof dp);
int lim = 1 << m;
dp[0] = 0;
for(int i = 0;i < n;i ++){
int t = 0;
for(int j = 0;j < k ;j ++) t |= (1 << can[i][j]);
for(int st = 0 ; st < lim; st ++){
if(dp[st] == -1) continue;
int nst = st | t;
if(dp[nst] == -1 || dp[nst] > dp[st] + 1) dp[nst] = dp[st] + 1;
}
}
cout << dp[lim - 1];
return 0;
}
试题 J: 组合数问题(杨辉三角求组合数 + 二维前缀和)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2010;
int c[N][N];
int s[N][N];
int main()
{
int T, k;
scanf("%d%d", &T, &k);
// 杨辉三角求组合数
for (int i = 0; i < N; i ++ )
for (int j = 0; j <= i; j ++ )
{
if (!j) c[i][j] = 1 % k;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % k;
if (!c[i][j]) s[i][j] = 1;
}
// 二维前缀和
for (int i = 0; i < N; i ++ )
for (int j = 0; j < N; j ++ )
{
if (i) s[i][j] += s[i - 1][j];
if (j) s[i][j] += s[i][j - 1];
if (i && j) s[i][j] -= s[i - 1][j - 1];
}
while (T -- )
{
int n, m;
scanf("%d%d", &n, &m);
printf("%d\n", s[n][m]);
}
return 0;
}