三角形面积 S = ( A B ∗ s + B C ∗ t + A C ∗ u ) / 2 = A B ∗ h / 2 S = (AB * s + BC * t + AC * u)/2 = AB * h / 2 S=(AB∗s+BC∗t+AC∗u)/2=AB∗h/2 (h为三角形在AB边上的高)
因为这是等边三角形,所以 s + t + u = h = 3 ∗ A B / 2 s+t+u = h = \sqrt3 * AB / 2 s+t+u=h=3∗AB/2
#include
#include
double xa,ya,xb,yb,xc,yc,xp,yp;
int main(){
while(~scanf("%lf%lf",&xa,&ya)){
scanf("%lf%lf",&xb,&yb);
scanf("%lf%lf",&xc,&yc);
scanf("%lf%lf",&xp,&yp);
double s=sqrt((xa-xb)*(xa-xb)+(ya-yb)*(ya-yb));
double h= 0.5*sqrt(3)*s;
printf("%.6lf\n",h);
}
return 0;
}
因为奶茶最便宜的都要2元,而珍珠的价格也是2元,而加第一份珍珠相当于2元买了同样的一杯奶茶,而第二份珍珠相当与2元买了2杯奶茶,所以肯定不会买两杯奶茶,只需要买一杯奶茶,剩下的钱全部加珍珠就能得到最大的满足感
#include
int price,love;
int main(){
int n;
while(~scanf("%d",&n){
int ans = 0;//最大的满足感
for(int i = 0; i < n; i++){
scanf("%d",&price);//价格
love = price;//满足感
while(price < 19){
love *= 2;price += 2;
}
ans = max(ans,love);
}
printf("%d\n",ans);
}
}
要想丑陋度最小,按照顺时针摆花 花的高度应该先上升后下降;当然先下降后上升也行
用数组表示既为数组是先上升后下降的,数组最后一个元素和第一个元素相邻
先假设花的高度为 [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] [1,2,3,4,5,6,7,8] [1,2,3,4,5,6,7,8]
先摆放高度为1的花 序列为 [ 1 , n u l l , n u l l , n u l l , n u l l , n u l l , n u l l , n u l l ] [1,null,null,null,null,null,null,null] [1,null,null,null,null,null,null,null] null表示该位置没有摆放花
然后把2和3摆放到nul的最左端和最右端,得到 [ 1 , 2 , n u l l , n u l l , n u l l , n u l l , n u l l , 3 ] [1,2,null,null,null,null,null, 3] [1,2,null,null,null,null,null,3]
接下来依次摆放高度为4和5的花 [ 1 , 2 , 4 , n u l l , n u l l , n u l l , 5 , 3 ] [1,2,4,null,null,null,5, 3] [1,2,4,null,null,null,5,3]
然后再摆放高度为6和7的花 [ 1 , 2 , 4 , 6 , n u l l , 7 , 5 , 3 ] [1,2,4,6,null,7,5, 3] [1,2,4,6,null,7,5,3]
最后摆放高度为8的花 [ 1 , 2 , 4 , 6 , 8 , 7 , 5 , 3 ] [1,2,4,6,8,7,5, 3] [1,2,4,6,8,7,5,3]
因为某朵花 a i a_i ai 要与两朵花 a i + 2 a_{i+2} ai+2 和 a i − 2 a_{i-2} ai−2 相邻,所以可以对排序后的花隔一个遍历,计算它们高度差的最大值。
注意当 n = 2 n = 2 n=2时,只有两朵花,直接计算它们的高度差。
#include
//这个是万能头文件,加入这一个就加了绝大部分算法竞赛需要的同文件
//例如stdio.h,iostream,algorithm,vector等等
//如果CB或者DEV C++没有这个头文件需要手动去网上找方式添加,
//在HDU和其他大部分OJ平台cpp提交都是支持这个头文件的
using namespace std;
typedef long long ll;
ll h[5005];
int main(){
int n;
while(~scanf("%d",&n)){
for(int i = 0 ; i < n ; ++i) scanf("%lld",&h[i]);
sort( h ,h + n);//这里是对数组h内元素排序,如果不懂的可以写个冒泡排序
ll i = 0,ans = h[1] - h[0];
while(i+2 < n){
ans = max(ans,h[i+2] - h[i]);
i++;
}
printf("%lld\n",ans);
}
}
把所有偶数除2一直除到奇数,统计次数即可
#include
//下面涉及到的位运算符号有兴趣的同学可以百度了解
int main(){
int n;
while(~scanf("%d",&n)){
long long a,ans = 0;
for(int i = 0 ; i < n ; ++i){
scanf("%lld",&a);
while(!(a & 1)){// &为位运算符 (a & 1) == (a % 2)
// !为取反 !false == true !true == false
++ans; a >>= 1;// >>为左移运算符,相当于 /2 即 (a>>1)==(a/2)
}
}
printf("%lld\n",ans);
}
}
一个文本的输入,一个字母的输出,可以一直处理到文件结尾。接下来统计计数即可。
#include
#define N 30
int a[N];
int main() {
char ch;
while (~scanf("%c", &ch)) {
if (ch >= 'a' and ch <= 'z')
++a[ch - 'a'];
}
int pos = -1, maxi = -1;
for (int i = 0; i < 30; ++i)
if (maxi < a[i]) {
maxi = a[i];
pos = i;
}
printf("%c\n", 'a' + pos);
return 0;
}
注意观察输入范围 [ 1 , 2 64 ) [1,2^{64}) [1,264),出这个题目的目的就是让做题人真实掌握 c / c p p c/cpp c/cpp中数据类型的数据范围。一个字节存在八个二进制位。但是注意可能存在一个符号位,带 u n s i g n e d unsigned unsigned前缀的数据类型才不存在符号位。
算法竞赛中基本需要的都被列举在上方,有几个建议,有关浮点数统一使用 double,当 double 精度都不够的时候使用 long double,整数输入以及计算的时候,自己时刻需要清醒的判断是否会溢出int,如果溢出需要转成 long long 去计算,涉及 1 e 9 + 7 1e9+7 1e9+7取模的时候也是需要先开 long long 计算。
回到这个题目,看到数据范围,使用 unsigned long long 就可以保存下去,不需要开大数去模拟。
使用变量保存之后,因为涉及 a − b a-b a−b操作,在无符号变量做减法时,需要小心,因为它不能计算到正确的负数答案。所以我们只需要判断一下 a , b a,b a,b大小是否需要输出负号,这个题目就写完了。
#include
using namespace std;
typedef unsigned long long ull;
ull a, b;
int main() {
cin >> a >> b; // scanf("%llu %llu",&a,&b);
if (b > a) { swap(a, b); cout << '-'; }
cout << a - b << endl;
return 0;
}
本题目的就是为了让你们学会计数排序。本题时间被我精心设计了,只能在时间复杂度 O ( n ) O(n) O(n)的情况下才能不超时。也就是卡掉了使用快速排序 s o r t ( ) sort() sort()方法。
本题的 n ≤ 1300000 n\leq1300000 n≤1300000,但是 a i ≤ 50 a_i\leq50 ai≤50,我们使用一个 c n t cnt cnt数组统计其中每个数字出现过几次。
接下来是不是只需要把 c n t cnt cnt数组从 1 1 1到 50 50 50全部遍历一遍就可以输出从小到大的序列了。
#include
#define N 51
int a[N];
int main() {
int n;
scanf("%d", &n);
int i, j;
for (i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
++a[x];
}
for (i = 1; i < N; ++i) {
for (j = 1; j <= a[i]; ++j)
printf("%d ", i);
}
puts("");
return 0;
}
首先不考虑任何优化的前提下使用三重循环依次判断时间复杂度 O ( n 3 ) O(n^3) O(n3)。
这个在 n ≤ 1000 n\leq1000 n≤1000时,最坏情况下需要枚举的循环次数达到了 1 e 9 1e9 1e9的规模。这在 1 1 1s的时间是不可能完成的。特别还有多组输入的情况。
1 1 1s的时间限制,极限的循环次数应该也就是 1 e 8 1e8 1e8了。但是也尽量不要达到这个规模,考虑优化自己的算法,因为当你的算法常数比较大的时候,也是会超时的。
回来这个题目尽量三重循环不行,我们能不能优化掉一层循环,我们最后还需要枚举最后一个数嘛?是不是固定前面两个最后一个数也就与之固定了呢?答案是肯定的。
那么我们就可以使用二分查找去数组中找那个符合的答案。但是二分查找前提是数组有序。排序的时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn),在使用快速排序的前提下,后面两层循环+二分查找的时间复杂度 O ( n 2 l o g n ) O(n^2logn) O(n2logn)。
很显然整个算法的时间复杂度就是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)了,所以说使用冒泡排序也可以通过。
#include
#include
#define N 1007
int a[N];
void BubbleSort(int n) {
bool flag = 0;
while (!flag) {
flag = 1;
for (int i = 1; i <= n - 1; ++i)
if (a[i] > a[i + 1]) {
int tmp = a[i + 1];
a[i + 1] = a[i];
a[i] = tmp;
flag = 0;
}
--n;
}
}
bool check(int n, int x) {
int l = 1, r = n;
while (l <= r) {
int mid = (l + r) >> 1; //保证了不溢出int
if (a[mid] == x) return true;
else if (a[mid] > x) r = mid - 1;
else l = mid + 1;
}
return false;
}
int main() {
int n, x;
while (~scanf("%d %d", &n, &x)) {
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
BubbleSort(n); //冒泡排序为后序二分答案做准备
int flag = 0;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
int c = a[i] * x * x + a[j] * x;
if (check(n, -c)) { //去a[]中二分查找是否存在a[pos] + c = 0
puts("YES");
flag = 1;
break;
}
}
if (flag) break;
}
if (!flag) puts("NO");
}
return 0;
}
#include
using namespace std;
#define N 1005
int main() {
int a[N];
int n, x;
while (cin >> n >> x) {
for (int i = 0; i < n; i++)
cin >> a[i];
bool flag = false;
sort(a, a + n); // algorithm库中的cpp快排函数,十分好用建议百度学习用法
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
int c = -(a[i] * x * x + a[j] * x);
if (binary_search(a, a + n, c)) { // algorithm库中的二分查找函数,查找区间中是否存在c,返回的bool值
flag = true;
break;
}
}
if (flag)
break;
}
if (flag)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}
还有一种。使用一个很大的桶,保证可以记录出现过的 c c c。这样我们就省略掉二分查找这个步骤,直接看桶中是否存在这个解。还有一个要注意的点就是,数组下标不能出现负数,所以我们需要使用整体后推的办法。也就是把 − 1000 -1000 −1000看作 0 0 0,把 0 0 0看作 1000 1000 1000,把 1000 1000 1000看作 2000 2000 2000去判断。这样就可以直接通过下标索引判断真假而找到答案。
这种做法时间复杂度 O ( n 2 ) O(n^2) O(n2),但是需要多开空间去标记。牺牲空间换时间。当 n ≤ 5000 n\leq5000 n≤5000时使用上方的做法就可能会超时了,需要掌握这种桶标记的方案。
#include
using namespace std;
const int maxn = 1e3 + 5;
int c[2500], a[1005];
int main() {
int n, x;
while (cin >> n >> x) {
bool flag = false;
memset(c, 0, sizeof(c));
for (int i = 0; i < n; ++i) {
cin >> a[i];
c[a[i] + 1000] = 1;
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
int tmp = 1000 - a[i] * x * x - a[j] * x;
if (tmp >= 0 && tmp <= 2000 && c[tmp]) {
flag = true;
puts("YES"); break;
}
if (flag) break;
}
}
if (!flag) puts("NO");
}
return 0;
}
首先先看每个人需要的金钱是不会改变的,那么这个题目题意翻译就是每一个点权值是恒定不变的,你需要查找区间 [ l , r ] [l,r] [l,r]的和。
接下来就要解释一个算法知识点了,叫做前缀和。
如果我需要查询 T T T次,每次都查询 [ 1 , n ] [1,n] [1,n]的和,如果每次你都从头算,那么需要算 T ∗ n T*n T∗n次,而且非常非常多的重复计算,当这个 T , n T,n T,n比较大的时候就会超时。
使用一个数组 s u m sum sum,如果原来数组是 a a a。那么我们规定, s u m i = ∑ j = 1 j = i a j sum_i = \sum_{j=1}^{j=i}a_j sumi=∑j=1j=iaj。
那么如果我们需要查找区间 [ l , r ] [l,r] [l,r]的和,那么对应的就是 s u m r − s u m l − 1 sum_r-sum_{l-1} sumr−suml−1。
再来看这个题目,也就是前缀和的应用了,虽然我考虑到主要是打算介绍,不使用前缀和也是可以通过的。
#include
typedef long long ll;
ll rnk[5] = { 0,5,17,43,149 };
ll a[50004];
ll sum[50004];
ll mypow(ll x, int y) { //整数幂避免使用math.h中的pow函数防止精度问题
ll res = 1;
while (y) {
res *= x;
--y;
}
return res;
}
int main() {
/*freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);*/
int n, ca = 0;
sum[0] = 0;
while (~scanf("%d", &n)) {
int id, mons;
for (int i = 1; i <= n; i++) {
scanf("%d %d", &id, &mons);
a[i] = rnk[id] * mypow(2, mons / 12);
// a[i] = rnk[id] << (t / 12) 左移运算符计算结果是相同的
sum[i] = sum[i - 1] + a[i]; //前缀和,可以直接找到[l,r]区间的和
}
printf("#Table %d\n%lldG\n", ++ca, sum[n]);
int m, l, r;
scanf("%d", &m);
while (m--) {
scanf("%d%d", &l, &r);
printf("%lldG\n", sum[r] - sum[l - 1]);
}
}
return 0;
}