7-1 区间选点
给定 N 个闭区间 [ai,bi], 请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
输入格式:
第一行包含一个整数 N , 表示区间数。
接下来 N 行,每行包含两个整数 ai,bi,表示一个区间的两个端点。
1≤N≤105,
−109≤ai≤bi≤109
输出格式:
输出一个整数,表示所需的点的最小数量。
输入样例:
3
-1 1
2 4
3 5
输出样例:
2
#include<bits/stdc++.h>
using namespace std;
bool cmp(pair<int, int>a1, pair<int, int>a2) {
if (a1.first == a2.first)return a1.second < a2.second;
return a1.first < a2.first;
}
int main() {
int n;
cin >> n;
pair<int, int>temp;
vector<pair<int, int>>ans;
for (int i = 0; i < n; i++) {
cin >> temp.first >> temp.second;
ans.push_back(temp);
}
sort(ans.begin(), ans.end(), cmp);
int count = 1; temp = ans[0];
for (int i=1;i<n;i++) {
if (ans[i].first > temp.second) {
count++;
temp = ans[i];
}
if (ans[i].second < temp.second) {
temp.second = ans[i].second;
}
temp.first = ans[i].first;
}
cout << count << endl;
return 0;
}
使用贪心排序求解
7-2 添加括号
现在给出一个表达式,形如 a1/a2/a3/.../an。
如果直接计算,就是一个个除过去,比如 1/2/1/4=1/8。
然而小A看到一个分数感觉很不舒服,希望通过添加一些括号使其变成一个整数。一种可行的办法是 (1/2)/(1/4)=2 。
现在给出这个表达式,求问是否可以通过添加一些括号改变运算顺序使其成为一个整数。
输入格式
一个测试点中会有多个表达式。
第一行 t ,表示表达式数量。
对于每个表达式,第一行是 n,第二行 n 个数,第 i 个数表示 ai。
2≤n≤10000,1≤t≤100,1≤ai≤231−1。
输出格式
输出 t 行。
对于每个表达式,如果可以通过添加括号改变顺序使其变成整数,那么输出 Yes
,否则输出 No
。
样例输入:
2
4
1 2 1 4
5
6 5 7 9 12
样例输出:
Yes
No
#include<bits/stdc++.h>
using namespace std;
bool kuohao(int n) {
int m, temp;
cin >> m >> temp;
temp = temp / (__gcd(temp, m));
for (int i=2;i<n;i++) {
cin >> m;
temp = temp / (__gcd(temp, m));
}
return temp==1;
}
int main() {
int n;
cin >> n;
int temp;
for (int i=0;i<n;i++) {
cin >> temp;
if (kuohao(temp)) {
cout << "Yes" << endl;
}
else cout << "No" << endl;
}
return 0;
}
找与第二个的因子能让最后除第二个为整数便可
7-3 山头狙击战
题目描述
小明为了掩护大部队,单枪匹马同敌人周旋,后来被敌人包围在某山头……等等,为什么怎么听怎么像狼牙山五壮士!不过不用着急,这次小明携带了足够的弹药,完全可以将涌上来的敌人一个一个干掉。小明是个神枪手,只要他的枪膛中有子弹,他就能将在他射程m(用从敌人位置到山头的直线距离算)以内的一个敌人瞬间射杀。但如果在射程内没有敌人,出于节约子弹考虑和面子问题,小明会等待敌人靠近然后射击。
正当小明为自己的强大而自我膨胀时,他忽然发现了一个致命的失误:他携带的枪是单发枪,每射出一发子弹都必须花k秒钟的时间装子弹。而凶残的敌人才不会花时间等你换子弹呢。他们始终在以1m/s的速度接近山头。而如果在一个敌人到达山头时小明无法将他击毙,那么我们可怜的小明就将牺牲在敌人的刺刀下。现在小明用心灵感应向你发出求助:要保住自己的性命并且歼灭所有敌人,小明最多只能用多少时间给枪装上一发子弹?
说明:假设一开始小明的枪中就有一发子弹,并且一旦确定一个装弹时间,小明始终会用这个时间完成子弹的装卸。希望你能帮助小明脱离险境。
输入格式
每组输入数据,第一行有两个整数n和m,(2≤n≤100,000; 1≤m≤10,000,000)n代表敌人个数,m代表小明的射程。
接下来有n行,每行一个整数mi,(1≤mi≤10,000,000),代表每个敌人一开始相对山头的距离(单位为米)。
输出格式
每组输出数据仅有一个整数,代表小明的换弹时间(单位为秒)。
样例输入
6 100
236
120
120
120
120
120
样例输出
25
#include<bits/stdc++.h>
using namespace std;
vector<int>ren;
bool jianyan(int k,int m) {
int now = 0;
for (int i=0;i<ren.size();i++) {
if (ren[i] > m+now) {
now = (ren[i] - m);
now += k;
}
else {
if (now > ren[i])return false;
now += k;
}
}
return now>0;
}
int main() {
int n, m;
cin >> n >> m;
int k=1;
ren=vector<int>(n);
for (int i=0;i<n;i++) {
cin >> ren[i];
}sort(ren.begin(), ren.end());
while (jianyan(k, m)) {
k *= 2;
}
int left = (k / 2),right=k;
int mid=left;
while(left<right) {
mid = (left + right) / 2;
if (jianyan(mid, m)){
left=mid+1;
}
else {
right=mid-1;
}
}
while (jianyan(mid, m)) {
mid++;
}
while (!jianyan(mid,m)) {
mid--;
}
if (mid <= 0)mid = 1;
cout << mid << endl;
return 0;
}
二分法求最大值即可;
7-4 一元三次方程
给定一个形如ax3+bx2+cx+d=0的一元三次方程。
已知该方程有三个不同的实数根(根与根之差的绝对值≥10−6),且根范围均在[p,q]之间,你需要解出这个方程的三个根。
输入格式:
第一行一个整数T(1≤T≤1000),表示有T组数据
接下来T行,每行6个实数,分别表示a,b,c,d,p,q
数据保证:−102≤p,q≤102,且对于∀x∈[p,q],−106≤f(x)≤106
输出格式:
输出三个实数,表示方程的三个解。
你的答案可以以任意顺序输出。
一个答案被认为是正确的,当且仅当其与标准答案的绝对误差不超过10−6
输入样例:
在这里给出一组输入。例如:
1
1.000000 -5.000000 -4.000000 20.000000 -10.000000 10.000000
输出样例:
在这里给出相应的输出。例如:
-2.000000 2.000000 5.000000
#include<bits/stdc++.h>
using namespace std;
double sanci(double a, double b, double c, double d, double left, double right) {
double mid = (left + right) / 2;
while (abs(a * mid * mid * mid + b * mid * mid + c * mid + d) > 1e-6) {
mid = mid - (a * mid*mid*mid + b * mid *mid+ c * mid + d) / (3.0 * a * mid * mid + 2.0 * b * mid + c);
}
return mid;
}
int main() {
int n;
cin >> n;
for (int i=0;i<n;i++) {
double a, b, c, d, p, q;
cin >> a >> b >> c >> d >> p >> q;
double left = ((1.0) * (-2 * b) + sqrt(4 * b * b - 4 * 3 * a * c)) / 6 / a;
double right= ((1.0) * (-2 * b) - sqrt(4 * b * b - 4 * 3 * a * c)) / 6 / a;
if (left > right)swap(left, right);
double x1 = sanci(a, b, c, d, p, left), x2 = sanci(a, b, c, d, left, right),x3=sanci(a, b, c, d, right, q);
cout << fixed << setprecision(6) << x1 <<' ' << x2<<' ' << x3 << endl;
}
return 0;
}
没考虑left与right谁大;
没传double值
7-5 线性变换
题目背景
小F有一个序列 a0,a1,a2,...,an−1,他决定进行 T 轮操作,来产生一个加密值 X。
在一开始,他有一个下标 P,一对线性变换系数 K、B,加密值 X 的初始值为 0
每一轮重复如下过程:
- X=X+aP
- P=(K×P+B)%n
请你帮他计算这个加密值 X 最后是多少。
输入格式
第一行输入五个整数 n,P,K,B,T(1≤n≤106,0≤P<n,0≤K,B≤106,0≤T≤1012)
第二行输入 n 个整数 a0,a1,a2...an−1(0≤ai≤106),表示这个序列。
输出格式
输出一个整数,表示最终的加密值X
输入样例1:
7 1 1 3 4
1 4 2 2 5 10 1
输出样例1:
12
输入样例2:
8 3 2 0 2
1 2 3 4 8 7 6 5
输出样例2:
10
#include<bits/stdc++.h>
using namespace std;
int main() {
long long n, p, k, b, t;
cin >> n >> p >> k >> b >> t;
vector<int>a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
vector<long long>huan;
huan.push_back(a[p]);
map<int,int>xunhuan;
xunhuan.emplace(p, 0);
p = (k * p + b) % n;
int i;
for (i = 1;; i++) {
if (xunhuan.find(p)!=xunhuan.end())break;
xunhuan.emplace(p,i);
huan.push_back(a[p]);
p = (k * p + b) % n;
}
i = (xunhuan.find(p))->second;
vector<int>start(huan.begin(), huan.begin() + i);
vector<int>end(huan.begin() + i, huan.end());
long long dec = min((long long)(start.size()), t);
long long x = accumulate(start.begin(), start.begin() + dec, 0ll);
t -= dec;
if (t > 0) {
int len = end.size();
long long tot = accumulate(end.begin(), end.end(), 0ll);
x += t / len * tot +
accumulate(end.begin(), end.begin() + t % len
, 0ll);
}
cout << x << endl;
/*
8 0 4 0 100
1 4 2 2 5 10 1 1
*/
return 0;
}
先找循环,然后累加
忘考虑环的第一个可能不在循环
P1028 [NOIP2001 普及组] 数的计算
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<int>ans;
int n;
cin>>n;
ans.push_back(1);
ans.push_back(1);
for(int i=2;i<=n;i++){
int temp=1;
for(int i1=1;i1<=i/2;i1++){
temp+=ans[i1];
}
ans.push_back(temp);
}
cout<<ans[n]<<endl;
return 0;
}
合法数据包括n 和n+f(n/2)+...+f0;
然后递推
P1192 台阶问题
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<int>ans={1,1};
int n,k;
cin>>n>>k;
/*
fn=fn-1+n-2+n-3+n-4...n-k;
f1=1;
*/
for(int i=2;i<=n;i++){
int temp=0;
for(int i1=1;i1<=k;i1++){
if(i-i1>=0){
temp+=ans[i-i1];
temp%=100003;
}
}
ans.push_back(temp);
}
cout<<ans[n]<<endl;
return 0;
}
fn=fn-1+n-2+n-3+n-4...n-k;
f1=1
P1044 [NOIP2003 普及组] 栈
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<long long>ans={1,1};
int n;
cin>>n;
for(int i=2;i<=n;i++){
ans.push_back(ans[i-1]*(4*i-2)/(i+1));
}
cout<<ans[n]<<endl;
return 0;
}
栈的出栈序列为卡特兰数 递推公式fn=fn-1*(4*(n-1)-2)/(n+1);
P1003 [NOIP2011 提高组] 铺地毯
#include <bits/stdc++.h>
using namespace std;
struct ditan{
int x,y;
int a,b;
void shuru(){
cin>>x>>y>>a>>b;
}
bool fugai(int a1,int b1){
if(a1>x+a||a1<x)return false;
if(b1>y+b||b1<y)return false;
return true;
}
};
int bian(vector<ditan>&ans,int x,int y){
for(int i=ans.size()-1;i>0;i--){
if(ans[i].fugai(x,y))return i;
}
return -1;
}
int main(){
int n;
cin>>n;
vector<ditan>ans(n+1);
for(int i=1;i<=n;i++){
ans[i].shuru();
}
int x,y;
cin>>x>>y;
cout<<bian(ans,x,y)<<endl;
return 0;
}
记录数据
从后往前检验即可;