模拟
1 1001 A+B Format
就是用%/ 注意,最后输出的时候printf("%03d",ans) 就可以保持三位左补0;
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
// 模拟
int main()
{
int a,b;
cin >> a >> b;
int c = a+b;
vector<int> ans;
int tmp = c;
do
{
ans.push_back(abs(tmp%1000));
tmp /= 1000;
}while(tmp);
if(c < 0)cout << '-';
if(c == 0)
{
cout << tmp;
return 0;
}
int i = ans.size() - 1;
printf("%d",ans[i--]);
for(; i >= 0; i--)
printf(",%03d",ans[i]); // 3表示输出三位数字 0表示左补充0 学到了!!
return 0;
}
2 1002 A+B for Polynomials
第一天死都过不了第2个测试点,后来知道原来错在:浮点数与0比较没有加fabs绝对值 。 本题直接浮点数==0 也能过,但是对一般题目而言浮点数与0比较还是统一用作差绝对值<一个极小数,(这个点某个夏令营也被问到了,当时短路了没回答上)原因是浮点数在电脑中是01二进制存储的。不是那么绝对精确。
AC代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#define eps 1e-9
using namespace std;
const int N = 1010;
float a[N];
int main()
{
int t = 2;
while(t--)
{
int k;
cin >> k;
for(int i = 0; i < k; i++)
{
int idx;
float x;
cin >> idx >> x;
a[idx] += x;
}
}
int num = 0;
for(int i = 0; i < N; i++)
if( fabs(a[i]) > eps)num++; // 测试点2一直过不了的原因是没有加fabs绝对值
cout << num;
for(int i = N-1; i >= 0; i--)
{
if(fabs(a[i]) > eps)
{
printf(" %d %.1f",i,a[i]);
}
}
return 0;
}
哈希
84 1084 Broken Keyboard
就一个简单hash,但我思路和代码能力不能支持几分钟敲出来
AC代码:
#include<iostream>
#include<string>
using namespace std;
int h[40];
int main()
{
string ori;
string now;
cin >> ori >> now;
for(int i = 0; i < ori.size();i++)
if(ori[i] >= 'a' && ori[i] <= 'z')ori[i] -= 32;
for(int i = 0; i < now.size();i++)
if(now[i] >= 'a' && now[i] <= 'z')now[i] -= 32;
for(int i = 0; i < now.size();i++)
{
if(now[i] >= 'A' && now[i] <= 'Z')h[now[i] - 'A']++; //0-25
else if(now[i] >= '0' && now[i] <= '9')h[now[i] - '0'+26]++; //26-35
else if(now[i] == '_')h[36]++;
}
for(int i = 0; i < ori.size();i++)
{
if(ori[i] >= 'A' && ori[i] <= 'Z') //0-25
{
if(h[ori[i] - 'A'] == 0)
{
printf("%c",ori[i]);
h[ori[i] - 'A'] = 1;
}
}
else if(ori[i] >= '0' && ori[i] <= '9')//26-35
{
if(h[ori[i] - '0'+26] == 0)
{
printf("%c",ori[i]);
h[ori[i] - '0'+26] =1;
}
}
else if(ori[i] == '_')
{
if(h[36] == 0)
{
printf("%c",ori[i]);
h[36]=1;
}
}
}
return 0;
}
92 1092 To Buy or Not to Buy
AC代码:
#include<iostream>
#include<string>
using namespace std;
const int N = 70;
int hash1[N];
int main()
{
string t,my;
cin >> t >> my;
for(int i = 0; i < my.size();i++)
{
if(my[i] >= '0' && my[i] <= '9') //0-9
{
hash1[my[i] - '0']++;
}
else if(my[i] >= 'a' && my[i] <= 'z') //10-35
{
hash1[my[i] - 'a'+10]++;
}
else //36-61
{
hash1[my[i] - 'A'+35]++;
}
}
for(int i = 0; i < t.size(); i++)
{
if(t[i] >= '0' && t[i] <= '9') //0-9
{
hash1[t[i] - '0']--;
}
else if(t[i] >= 'a' && t[i] <= 'z') //10-35
{
hash1[t[i] - 'a'+10]--;
}
else //36-61
{
hash1[t[i] - 'A'+35]--;
}
}
int num = 0;
for(int i = 0; i < 62; i++)
{
if(hash1[i] > 0)num+=hash1[i];// 空缺的
}
if(num)
{
cout << "No " << num;
}
else
{
cout << "Yes " << t.size() - my.size();
}
return 0;
}
/*
ppRYYGrrYBR225
YrR8RrY
*/
贪心
70 1070 Mooncake
测试点2好像是一定开double才行,测试点3不知道什么问题一直过不了。
#include<iostream>
#include<string.h>
#define eps 1e-6
using namespace std;
const int N = 1010;
double hold[N];
double price[N];
double singlep[N];
bool vis[N];
int main()
{
int type;
double need;
scanf("%d %lf", &type, &need);
for(int i = 0; i < type; i++)
// cin >> hold[i];
scanf("%lf",&hold[i]);
for(int i = 0; i < type; i++)
// cin >> price[i];
scanf("%lf",&price[i]);
for(int i = 0; i < type; i++)
singlep[i] = price[i]/hold[i];
double ans = 0;
while(need>eps)
{
double max_sp = -0xffff; //找到最贵的
int max_idx = 0;
for(int i = 0; i < type; i++)
{
if(!vis[i])
{
// cout << "singlep[i]:" << singlep[i] << endl;
if(singlep[i] >= max_sp)
{
max_sp = singlep[i];
max_idx = i;
}
}
}
vis[max_idx] = 1;
if(hold[max_idx] >= need)
{
if(hold[max_idx] == need)
{
ans += price[max_idx];
}
else ans += singlep[max_idx]*need;
need = 0;
break;
}
else
{
need -= hold[max_idx];
ans += price[max_idx];
// cout << "need " << need << "ans " << ans << endl;
}
}
printf("%.2lf",ans);
}
/*
3
982
32 888 32
9 1000 22
*/
37 1037 Magic Coupon
比较容易实现的贪心。
AC代码:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int a[N];
int b[N];
bool cmp(int x,int y)
{
return x>y;
}
int main()
{
// 贪心的策略:
/*
(1)两行从大到小排序
(2)正的和正的乘
(3)之后从右边开始负的和负的乘
*/
int n;
cin >> n;
for(int i = 0; i < n; i++)
cin >> a[i];
int m;
cin >> m;
for(int i = 0; i < m; i++)
cin >> b[i];
sort(a,a+n,cmp);
sort(b,b+m,cmp);
long long ans = 0;
for(int i = 0;a[i] > 0; i++ )
if(i < m && b[i] > 0)
{
ans += a[i] * b[i];
// cout << "ans" << ans << endl;
}
int j = m-1;
for(int i = n-1; a[i] < 0 && i >= 0; i--)
{
if(j >= 0 && b[j] < 0)
{
ans+=a[i]*b[j--];
}
else
break;
}
cout << ans << endl;
return 0;
}
38 1038 Recover the Smallest Number
一开始我的思路:
/*
初步思路是
从高到低位数依次升序
如果两个数前面k位都相同
如果两个都有k+1位 那就按照k+1位置升序
如果一个有大于k位 另一个只有k个 则前者的k+1与另一个的第1位比较 升序
*/
但后来在cmp的return一个很细节的语法卡了很久。解决之后。
一个测试点
2 0000 0000 这个要处理
之后发现对于
2 00002 1000 别人的代码答案竟然是21000 不是10002 明白了题意理解错了,前导0也加入排序中不过最后输出时去掉前导0即可。
错误代码:
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int N = 1e4+10;
string a[N];
int num = 0;
int tmpa[10];
int tmpb[10];
int cnta;
int cntb;
/*
初步思路是
从高到低位数依次升序
如果两个数前面k位都相同
如果两个都有k+1位 那就按照k+1位置升序
如果一个有大于k位 另一个只有k个 则前者的k+1与另一个的第1位比较 升序
*/
void split(int x,int* u,int &cnt)
{
while(x)
{
u[cnt++] = x%10;
x /= 10;
}
int l = 0,r = cnt - 1;
while(l < r)
{
swap(u[l],u[r]);
l++;
r--;
}
return;
}
bool cmp(int x,int y)
{
memset(tmpa,0,sizeof(tmpa));
memset(tmpb,0,sizeof(tmpb));
cnta = 0;
cntb = 0;
split(x,tmpa,cnta);
split(y,tmpb,cntb);
int i = 0;
while(i < cnta && i < cntb)
{
if(tmpa[i] != tmpb[i])
{
return tmpa[i]<tmpb[i]; // 321 327
}
else
{
i++;
}
}
if(i < cnta) // 3215 321
{
return tmpa[i]<tmpb[0];
}
else if(i < cntb) // 321 3211
{
return !(tmpb[i]<tmpa[0]);
}
return 1;// 两个数相等
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
int x;
cin >> x; // 00002 1000 应该输出21000 而不是100021
if(x) //去掉0
a[num++] = x;
}
sort(a,a + num,cmp);
if(n != 0 && num == 0)
{
cout << 0 << endl;
return 0;
}
for(int i = 0; i < num; i++)
printf("%d",a[i]);
return 0;
}
/*
5
1002 0003 9 3218 0 32
*/
看了题解,学习了别人的思想直接用string来做,两个string 选可以组成更小数的排序。AC代码:
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
const int N = 1e4+10;
string a[N];
bool cmp(string x,string y)
{
return x+y < y+x;
}
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
{
string x;
cin >> x; // 00002 1000 应该输出21000 而不是100021
a[i] = x;
}
sort(a,a + n,cmp);
string fin = "";
for(int i = 0; i < n;i++)fin += a[i];
int flag = 0;
for(int i = 0; i < fin.size();i++)
{
if(fin[i] != '0' || (fin[i] == '0' && flag))
{
cout << fin[i];
flag = 1;
}
else
{
if(!flag)continue;
}
}
if(!flag)cout << 0;
return 0;
}
/*
5
1002 0003 9 3218 0 32
*/
二分
85 1085 Perfect Sequence
一个二分,注意一个关于long long的语法知识
long long res= a[i]*p; // 这样错误 因为右边没有强制转换会爆int!
long long res= (long long)a[i]*p; // 这样正确。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int a[N];
int n;
int dic(int i,int j,long long res)//找小于等于res的最大idx
{
int l = i;
int r = j;
while(l<r)
{
int mid =(l+r+1)>>1;
if(a[mid] > res)r = mid - 1;
else l = mid;
}
return (l - i + 1);
}
int main()
{
int p;
cin >> n >> p;
for(int i = 0; i < n; i++)
scanf("%d",&a[i]);
sort(a,a+n);
int ans = 0;
for(int i = 0; i < n; i++)
{
long long res= (long long)a[i]*p;
// long long res = a[i];
// res*=p;
int tmp = dic(i,n-1,res); //找小于等于res的最大idx
ans = max(ans,tmp);
}
cout << ans << endl;
return 0;
}
10 1010 Radix
初步思路:
把确定进制的数转成10进制的,设为 p,另外一个数,设为 u,二分查找它的进制,设为 k。
坑:(学习别的题解)
(1)k的下限:不是2,是因为在二分中某个tmpk进制 可能出现太小 不满足u(如,u=9 但是tmpk是2进制)。是u的值的最高位表示的数+1。
(2)k的上限: 不是我一开始预想的最大进制36。那 k 的最大值是?k最大值是p。
原因是:当u只有一位数时,那么u最大可以取z,但z最小的满足的进制就是36 再往上没有意义,都是表示35。当u不止一位数时候,先考虑它两位数,那最小的表示就是10,未来使得(10)k = p,那么k就是p即可,当u最大时候k越小,所以k的最大值是p。其他题解有说是p+1,是考虑到如:10 a 1 10 这样的样例,他们的代码过不了 但我的代码是可以的。所以max_k = p,写成max_k = max(min_k,p)也行。
(3)考虑 0 0 1 10 , 8 8 1 10 样例
(4)二分过程中,mid进制的数(long long进制的数)会爆long long,变成负数。
(5)很多过程中与long long涉及的变量全都要longlong
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
// 坑4 很多过程中与long long涉及的变量全都要longlong
long long turn_radix(string x,long long p1)
{
string tmpx = x;
long long ans = 0;
long long p = 1;
while(!x.empty())
{
char a = x.back();
x.pop_back();
int a1 = 0;
if(a >= '0' && a <= '9')a1 = a - '0';
else a1 = a - 'a'+10;
ans += a1*p;
p*=p1;
}
// cout << p1 << " 进制的 " << tmpx << " 是:" << ans << endl;
return ans;
}
int main()
{
string n1,n2;
int tag,radix;
cin >> n1 >> n2 >> tag >> radix;
long long tmp_n1 = 0;
string tmp_n2 = "";
if(tag == 1)
{
// n1是radix进制
tmp_n1 = turn_radix(n1,radix);
tmp_n2 = n2;
}
else
{
// n2是radix进制
tmp_n1 = turn_radix(n2,radix);
tmp_n2 = n1;
}
// 二分判断进制
char t = '0';
for(int i = 0; i < tmp_n2.size();i++)
if(tmp_n2[i] > t)t = tmp_n2[i];
long long l = 0; // 坑1:最小进制为最高位代表的数+1
if(t >= '0' && t <= '9')l = t - '0' + 1;
else l = t - 'a' + 10 + 1;
if(l < 2)l = 2;
long long r = max(l,tmp_n1); // 坑2:最大进制为已知的十进制数
// long long r = tmp_n1; // 也能过
while(l < r)
{
long long mid = (l + r) >> 1;
long long tmpr = turn_radix(tmp_n2,mid); // 坑3 :mid进制的数(long long)会爆long long
if(tmpr >= tmp_n1 || tmpr < 0)
r = mid;
else l = mid + 1;
}
if(turn_radix(tmp_n2,l) == tmp_n1 )
cout << l << endl;
else
cout << "Impossible" << endl;
return 0;
}
双指针
29 1029 Median
归并排序的合并两个有序序列。测试点3,6坑: 坑: 测试点3,6:一个数组很小 一个很大 小的那个每到中位数就结束了
#include<iostream>
using namespace std;
const int N = 2e5+10;
int a[N];
int b[N];
int main()
{
int n;
cin >> n;
for(int i = 0; i < n; i++)
scanf("%d",&a[i]);
int m;
cin >> m;
for(int i = 0; i < m; i++)
scanf("%d",&b[i]);
int k;
if((n+m)%2 == 1)k = (n+m)/2+1;
else k = (n+m)/2;
int i = 0;
int j = 0;
int u = 0;
int ans = 0;
while(u < k && i < n && j < m)
{
if(i < n && a[i] <= b[j]){ans = a[i];i++;u++;}
else if(j < m && a[i] > b[j]) {ans = b[j];j++;u++;}
}
while(u < k && i < n){ans = a[i];i++;u++;} // 坑: 测试点3,6:一个数组很小 一个很大 小的那个每到中位数就结束了
while(u < k && j < m){ans = b[j];j++;u++;}
cout << ans << endl;
}
新思路
1 左右两次遍历大法
1093 Count PAT's
暴力肯定超时,对于每个A的答案就是左边的P的个数*右边T的个数。所以一开始先把每一个i的左边及自身占的 P 的个数存到一个数组里面。T 同理。 只不过可以在和遍历A求答案的时候顺路计算了。AC代码:
#include<iostream>
using namespace std;
const int N = 1e5+10;
const int MOD = 1000000007;
typedef long long ll;
int leftpnum[N];
int main()
{
string x;
cin >> x;
for(int i = 0; i < x.size();i++)
{
if(i == 0)
{
if(x[i] == 'P')leftpnum[i] = 1;
else leftpnum[i] = 0;
}
else
{
if(x[i] == 'P')leftpnum[i] = leftpnum[i-1]+1;
else leftpnum[i] = leftpnum[i-1];
}
}
ll ans = 0;
int num = 0;
for(int i = x.size()-1;i >= 0; i--)
{
if(x[i] == 'T')num++;
else if(x[i] == 'A') ans += (ll)(leftpnum[i]*num)%MOD;
}
cout << ans%MOD << endl;
}
1101 Quick Sort
我一开始思路就是归并排序求逆序对的板子改一下,但是查了一下一个题最多开1.6e7的内存。我这个解法要1e5+1e9爆了,如果改成map 查询的时候还要再乘上logn,即(n*logn*logn) 会爆。代码如下:
#include<iostream>
#include<map>
using namespace std;
const int N = 1e5+5;
//const int M = 1e9+5;
//一般算法题就 撑死1.6e7
int a[N];
map<int,int> t;
int tmp[N];
void guibing(int l,int r)
{
if(l >= r)return;
int mid = (l+r) >> 1;
guibing(l,mid);
guibing(mid+1,r);
int i = l;
int j = mid+1;
int k = 0;
while(i <= mid && j <= r)
{
if(a[i] <= a[j])tmp[k++] = a[i++];
else{
t[a[i]] = 0;
t[a[j]] = 0;
tmp[k++] = a[j++];
}
}
while(i <= mid)tmp[k++] = a[i++];
while(j <= r)tmp[k++] = a[j++];
for(int u = 0,i = l; u < k ; u++)a[i++] = tmp[u];
return;
}
int main()
{
int n;
cin >> n;
int max_idx = -1;
for(int i = 0; i < n; i++)
{
scanf("%d",&a[i]);
t[a[i]] = 1;
max_idx = max(max_idx,a[i]);
}
guibing(0,n-1);
int num = 0;
for(int i = 1; i <= max_idx; i++)
if(t[i])num++;
cout << num << endl;
// for(int i = 0; i < n; i++)
// {
// printf("%d ",a[i]);
// }
//
int ok = 0;
for(int i = 1; i <= max_idx; i++)
if(t[i])
{
if(!ok)
{
cout << i ;
ok = 1;
}
else
cout << " " << i ;
}
return 0;
}
后面改成答案的解法,和1093思路雷同的。
AC代码:
还有几个点没过made 8.7
数学
1 分数
1081 Rational Sum
具体分数的表示,化简,加减乘除,输出见算法笔记。
【语法】
过程中我遇到一个及其傻逼的语法问题,在scanf中多写了一个 \n,导致每次输入完第二个了才输出上一个。像下面这个:
scanf("%lld/%lld\n",&tmp.up,&tmp.down); // scanf 不要写/n 不然会导致输入错误!!!!!
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 120;
class fra
{
public:
ll up;
ll down;
fra(ll a = 0,ll b = 1)
{
this->up = a;
this->down = b;
}
};
fra tobe_fraction(fra x)
{
// 负数
if(x.down < 0)
{
x.up *= -1;
x.down *= -1;
}
// 0
if(x.up == 0)x.down = 1;
else // 非0就约减
{
ll tmp = __gcd(abs(x.up),abs(x.down));
x.up /= tmp;
x.down /= tmp;
}
return x;
}
fra add(fra a,fra b)
{
fra tmp;
tmp.up = a.up*b.down + a.down*b.up;
tmp.down = a.down*b.down;
return tobe_fraction(tmp);
}
int main()
{
int n;
cin >> n ;
fra ans;
while(n--)
{
fra tmp;
// scanf("%lld/%lld\n",&tmp.up,&tmp.down); // scanf 不要写/n 不然会导致输入错误!!!!!
scanf("%lld/%lld",&tmp.up,&tmp.down);
ans = add(ans,tmp);
}
ans = tobe_fraction(ans);
if(ans.down == 1)cout << ans.up;
else if(ans.up > ans.down)
{
cout << ans.up/ans.down << " " << abs(ans.up)%ans.down << "/" << ans.down;
}
else
{
cout << ans.up << "/" << ans.down;
}
return 0;
}
2 素数
1015 Reversible Primes
题目大意是一个数a必须是素数。然后将这个数a转为D进制数b,再将这个D进制数b反转成数c,将反转后的数c再转为十进制数,要求这个数依旧是素数。题目比较简单,实现判断素数、进制转换、数的反转这三个功能即可。注意特判:1不是素数!!
AC代码:
#include<iostream>
using namespace std;
bool is_prime(int x)
{
if(x == 1)return 0; //特判1不是素数
for(int i = 2; i < x; i++)
if(x % i == 0)return 0;
return 1;
}
int main()
{
while(1)
{
int a,b;
cin >> a;
if( a < 0)break;
cin >> b;
// cout << a << " " << b;
// a是素数
if(!is_prime(a))
{
cout << "No" << endl;
continue;
}
// a 转成 b 进制的数x 翻转一下 再转成10进制的y 判断是不是素数
string x = "";
while(a)
{
x += ('0' + a%b);
a /= b;
}
// cout << b << "进制的" << a << "为:" << x << endl;
int y = 0;
int p = 1;
for(int i = x.size() - 1; i >= 0; i--)
{
y += (x[i] - '0')*p;
p *= b;
}
// cout << "y: " << y << endl;
if(is_prime(y))
{
cout << "Yes" << endl;
}
else cout << "No" << endl;
}
return 0;
}
1078 Hashing
知识点:二次探测(仅具有正增量,即1^2,2^2,3^2........)用于解决碰撞。
【注意点1】step从1.....ms-1。
【注意点2】新地方 = (碰撞地方(固定的) + step^2 )%ms
AC代码:
#include<iostream>
using namespace std;
const int N = 1e4 + 10;
int t[N];
bool vis[N];
bool is_prime(int x)
{
if(x == 1)return 0; //特判1不是素数
for(int i = 2; i < x; i++)
if(x % i == 0)return 0;
return 1;
}
int main()
{
int ms,n;
cin >> ms >> n;
for(int i = 0; i < n; i++)
cin >> t[i];
while(!is_prime(ms)){ms++;}
for(int i = 0; i < n; i++)
{
if(i != 0)cout << ' ';
int tmp = t[i] % ms;
if(!vis[tmp])
{
vis[tmp] = 1;
cout << tmp;
}
else
{
int step = 1;
int ok = 0;
int old = tmp;
// 发生碰撞
for(int i = 1; i < ms; i++) // 【注意点1】1.... ms
{
if(vis[tmp])
{
//tmp = (tmp + step*step)%ms; 错误
tmp = (old + step*step)%ms; // 【注意点2】碰撞地方 + step^2
step++;
}
else
{
ok = 1;
vis[tmp] = 1;
cout << tmp;
break;
}
}
if(ok == 0) cout << '-';
}
}
return 0;
}
3 质因子分解
1059 Prime Factors
质因子分解的板子题目:
// 【细节1】x除的时候,for的边界是不停动态变化
//【细节2】x有可能有且仅有一个大于根号x的质因子
坑:特判0和1
#include<bits/stdc++.h>
using namespace std;
// 分解质因子板子题目
typedef long long ll;
int main()
{
ll x;
cin >> x;
int ok = 0;
cout << x << '=';
if(x <2)
{
cout << x ; // 【坑】某个测试点的坑
return 0;
}
for(ll i = 2; i <= x / i; i++) // 【细节1】x是不停动态变化
{
if(x % i == 0)
{
if(ok == 1)cout << '*';
int p = 0;
while(x % i == 0)
{
x /= i;
p++;
}
cout << i;
if(p > 1)cout << '^' << p;
ok = 1;
}
}
if(x > 1)
{
if(ok)cout << "*" << x ;
else cout << x; // 【细节2 】x有可能有且仅有一个大于根号x的质因子
}
return 0;
}
1096 Consecutive Factors
学习题解后的,我感觉难点在于看懂题目。就是找输入n的最长连续因子(这些因子乘起来小于等于n。)
AC代码:
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int l = 0;
int len = 0;
for(int i = 2; i <= n / i; i++)
{
if(n % i == 0)
{
int j = i;
int n1 = n; //
while(n1 % j == 0)
{
n1 /= j; // 注意这里要把n自除掉 是n的连续因子 但是乘起来不能超过n 比如12 不能是2*3*4
j++;
}
if(j-i > len)
{
l = i;
len = j - i;
}
}
}
if(len == 0)
{
cout<< 1 << endl << n <<endl;
}
else
{
cout << len << endl;
cout << l ;
for(int i = 1; i < len; i++)
cout << '*' << l+i;
}
return 0;
}