题目有点多,但不算难,好几道属于裸题。
题目一
Q哥在玩一个游戏,他面对一共n个怪物,击杀第i个怪物需要花费ci的血量,可获得wi的金币,在游戏开始Q哥可以花费金币来购买血量,那么游戏结束Q哥最多可以赚多少金币(我的理解:相当于求纯利润)
输入
第一行两个数字n和m,表示怪物数量和每个金币能够购买的血量。
接下来n行,每行两个数字ci和wi。
1<=n<=1000
1<=wi<=100
1<=m<=1e9
1<=ci<=1e6
输出
一个数字表示Q哥最大收益
样例一
输入
3 2
1 1
1 10
3 1
输出
10
开局花一块买2点血,击杀1和2获得11个金币,去掉成本净赚10。
样例二
输入
1 2
3 1
输出
0
亏本买卖选择不做。
分析
笔试的时候没想出来,看评论区学习的做法。
以为是背包然后一直纠结这个m太大了,其实压根不需要背包,这里可以购买的血量本来就没有上限,本质上就是尽可能把赚到钱的怪物都击杀了,如果击杀了能赚到钱的怪物以后还有血量多,就可以把本来不赚钱的怪物杀了,把这题联想到买卖上来就很容易思考了。
首先根据wi/ci的值排序,显然该值越大越赚钱。然后模拟一下买卖的过程,更新最大收益。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
struct Node
{
int cost, val;
double p;
}guai[1010];
int n, m;
bool cmp(const Node& a, const Node& b)
{
return a.p > b.p;
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++){
cin >> guai[i].cost >> guai[i].val;
guai[i].p = 1.0*guai[i].val / guai[i].cost;
}
int ans = 0;
sort(guai, guai+n, cmp);
int blood = 0;
int buy = 0;
int sum = 0;
for (int i = 0; i < n; i++){
if (blood < guai[i].cost){
buy = (guai[i].cost - blood) / m;
if ((guai[i].cost - blood)%m) buy++;
blood = blood + m*buy - guai[i].cost;
}
else {
buy = 0;
blood -= guai[i].cost;
}
sum += guai[i].val;
sum -= buy;
ans = max(ans, sum);
}
cout << ans << endl;
return 0;
}
题目二
求抛物线y^2 = 2Ax与直线y=Bx+C所围成的封闭图形面积,若图形不存在,则输出0。
输入
第一行一个T,表示测试组数;
接下来三个正整数A,B,C;
1<=T<=1000
1<=A,B<=100
-100<=C<=100
输出
一个浮点数,允许误差在1e-4范围内。
样例
输入
1
1 1 -6
输出
31.2481110540
分析
经典的定积分题目,相信考过研的同学肯定都很熟悉,既然正着看比较难受(y^2=2Ax不是一个一一映射),那就把图翻过来,以y轴为水平轴,就是个很明显的两个函数(一一映射)围成的区域。
y1和y2用求根公式计算。
这里A,B,C都是正整数,所以没有围成图形的情况就是A<=2BC
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int A, B, C;
double get(double y)
{
return y*y/2/B - C*y/B - y*y*y/6/A;
}
int main()
{
int T; cin >> T;
while (T--){
cin >> A >> B >> C;
if (A-2*B*C <= 0){
cout << 0 << endl;
continue;
}
double y1 = (1.0*A-1.0*sqrt(A*A-2*A*B*C)) / B;
double y2 = (1.0*A+1.0*sqrt(A*A-2*A*B*C)) / B;
double s = get(y2)-get(y1);
printf("%.5f\n", s);
}
return 0;
}
题目三
现在在一个监狱里,有n个房间连在一排,编号从1到n,每一个房间内有一个人。
现在要每个房间的人选一个1到m之间的数字,若相邻房间选择的数字一样则会发生冲突。
请问有多少种情况下有可能发生冲突。
输入
第一行两个整数m和n,表示数字范围和房间数量。
1<=n<=1012
1<=m<=108
输出
输出方案数量,答案对100003取余。
样例
输入
2 3
输出
6
分析
数学的排列组合问题,显然求不发生冲突的方案数更加方便,第一个人有m种选法,第二个人有m-1种,第三个第四个到第n个人都有m-1种选法,方案数为m*(m-1)(n-1),总的方案数有mn种,减一下就是答案。
但是n很大,所以必须用快速幂。快速幂其实很容易理解,就是看二进制下当前位是否为1,如果是就把结果乘以当前的base。
有一个需要注意的点,在进行减法的时候,因为减完还要进行取余,而负数是无法取余的,所以要在减完以后加上100003
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long LL;
const LL mod = 100003;
LL n, m;
int main()
{
cin >> m >> n;
LL num1 = 1;
LL tmp = m;
LL cnt = n;
while (cnt)
{
if (cnt&1) num1 = num1*tmp%mod;
cnt >>= 1;
tmp = tmp*tmp%mod;
}
LL num2 = 1;
tmp = m-1;
cnt = n-1;
while (cnt)
{
if (cnt&1) num2 = num2*tmp%mod;
cnt >>= 1;
tmp = tmp*tmp%mod;
}
num2 = num2 * m % mod;
LL ans = (num1- num2 + mod) % mod;
cout << ans << endl;
return 0;
}
题目四
有n个物品,每个物品有k个属性,第i件物品的第j个属性用一个正整数表示为ai,j,两个不同的物品i,j被称为完美对当且仅当ai,1+aj,1 = ai,2+aj,2 = ai,k+aj,k,求完美对的个数。
输入
第一行n,k。
接下来n行,第i行k个数字表示ai,1,ai,2,…,ai,k。
1<=n<=105,2<=k<=10,1<=ai<=100
输出
一个数字表示答案
样例
输入
5 3
2 11 21
19 10 1
20 11 1
6 15 24
18 27 36
输出
3
分析
这题没时间想了,暴力拿了30%
看评论区的思路是把ai,1,ai,2-ai,1,…,ai,k-ai,1作为匹配特征,找这个特征的相反数组成的特征的数量。
题目五
现在有107个用户,编号1~107,已知m对关系,每一对关系给你两个数x,y,代表编号为x的用户和编号为y的用户在一个圈子中,假如A和B在一个圈子中,B和C在一个圈子中,那么ABC就在一个圈子中,想知道最多的一个圈子有多少用户。
输入
第一行一个T,表示T组测试数据
每一组测试数据,第一行一个n表示n对关系
接下来n行每一行两个数x和y,表示关系
1<=T<=10
1<=n<=105
1<=x,y<=10^7
输出
对于每一组数据,输出圈子内最多人数
分析
并查集裸题,107有点大,但是因为n只有10^5所以可以离散化一下,但是我在写的时候图快(做不完了要)只记录了一下最大最小编号来稍微优化,居然过了。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
using namespace std;
int f[10000010];
int find(int x)
{
if(f[x]!=x)
f[x]=find(f[x]);
return f[x];
}
void work(int a,int b)
{
int tx = find(a);
int ty = find(b);
f[ty] = tx;
return ;
}
int a[100010], b[100010];
int main()
{
int T; cin >> T;
int n, minx = 10000001, maxx = 0;
map<int, int> mp;
while (T--){
cin >> n;
mp.clear();
for (int i = 1; i <= n; i++){
cin >> a[i] >> b[i];
minx = min(minx, a[i]);
minx = min(minx, b[i]);
maxx = max(maxx, a[i]);
maxx = max(maxx, b[i]);
}
for (int i = minx; i <= maxx; i++)
f[i] = i;
for (int i = 1; i <= n; i++)
work(a[i], b[i]);
int ans = 0;
for (int i = minx; i <= maxx; i++){
int father = find(i);
mp[father]++;
ans = max(ans, mp[father]);
}
cout << ans << endl;
}
return 0;
}