杂类+思维题+贪心 不知道是啥题
绝对值不等式 |x-a|+|x+b|>=|a-b| 去到等号 x必须在|a-b|中间 所以选择每个点到某个x点的距离最小值 这个x必须去到中位数的距离
求出 子集和的不能组成和的最小正整数
当前已经可以组合出[1,x]未用的数字中最小的是a,有两种情况
a > x + 1那么x+1就无法组合出
a<=x+1,那么就可以把当前的集合扩展为[1,x+a]
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std ;
using ll = long long ;
const int N = 1e5 + 100 ;
int n ;
int a[N] ;
int main(){
scanf("%d",&n) ;
for(int i = 1 ; i <= n ; i ++) scanf("%d",&a[i]) ;
sort(a + 1,a + n + 1) ; // 排序
ll tot = 0 ;
for(int i = 1 ; i <= n ; i ++){
if(a[i] <= tot + 1) tot += a[i] ; // 可以加入 a_i
else break ;
}
printf("%lld\n",tot + 1);
return 0 ;
}
贪心 跑到尽量远的地方换车https://ac.nowcoder.com/acm/contest/37160/F
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl "\n"
const int max_n = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
pair<int, int> pii[max_n];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, t;
cin >> n >> m >> t;
priority_queue<int> pq;
for (int i = 0; i < m; i++)
cin >> pii[i].first >> pii[i].second;
sort(pii, pii + m);
int ans = 0;
for (int i = 0; i < m; i++)
{
if (pii[i].first > t)//这个换货点已经超过了
{
//有店 并且 最远距离超过了 而且保证这次换车足够到达 下次的换车点
if (pq.size() && pq.top() >= pii[i].first){
t = pq.top(), ans++;
cout<<"堆里面靠近断货点最近点的是"<<pq.top() <<" "<<"超过挺火点的是"<<pii[i].first<<endl;
}
else
{
//如果没有店了
cout << "-1" << endl;
return 0;
}
}
pq.push(pii[i].first + pii[i].second);
}
//如果后面没有店了并且当前到达不了目的地
if (t < n)
t = pq.top(), ans++;
if (t >= n)
cout << ans << endl;
else //如果所有的店都跑不到
cout << "-1" << endl;
return 0;
}
猜拳 https://www.acwing.com/problem/content/1803/
但不知道编号顺序 求能赢多少局
根据我们剪刀石头布的规则,两个相邻的手势有胜负关系。也即 (1,2),(2,3),(3,1) 均有胜负关系。观察发现,当我们把编号都减去 1 后,也即三种手势分别为 (0,1,2)(0,1,2) 时,可以通过 (x+1)%3 获取 x 手势的下一个相邻手势。所以,我们分别用 a,b 记录两种胜负关系,最终输出两种关系胜场中较大值即可。6中情况实际上 是 2种情况
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
int a = 0, b = 0;
for(int i = 0; i < n; ++i) {
int x, y;
cin >> x >> y;
x--, y--;
// 当 x + 1 能赢 x 时 布 剪刀 石头
if((x + 1) % 3 == y) a++;//
// 当 x 能赢 x + 1 时 石头 剪刀 布
if(x == (y + 1) % 3) b++;
}
cout << max(a, b) << endl;
return 0;
}
岛
水位每上涨一个高度差后对数组中数的关系有怎样的影响
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define x first
#define y second
const int N = 1e5+10;
typedef pair<int, int> PII;
int h[N];
PII q[N];
int main()
{
int n;cin>>n;
for (int i = 1; i <= n; i ++ ) cin>>h[i];
n=unique(h+1,h+n+1)-h-1;//unique去除旁边相同的元素返回数组最后一个位置后一个的指针,-1-数组名 获得剩余目标的指针
h[n+1]=0;
for (int i = 1; i <= n; i ++ ){
q[i]={h[i],i};
}
sort(q+1,q+n+1);//以高度排序
int res=1,cnt=1;
for (int i = 1; i <= n; i ++ ){//一个个高度处理
int k=q[i].y;//从高度低的开始,k为坐标
if(h[k]>h[k-1]&&h[k]>h[k+1]) cnt--;//对于k这个住,只有左边一个和右边一个住满足这个条件才可以
else if(h[k]<h[k-1]&&h[k]<h[k+1]) cnt++;
if(q[i].x!=q[i+1].x) //只有当这条线和后一条线不同的时候才改变,即考虑最后一个某个高度的元素
res=max(res,cnt);
}
cout<<res;
return 0;
}
困牛排序 https://www.acwing.com/problem/content/description/1698/
难点在于想到 从后面往前面找到的第一个逆序的位置 就是 答案
因为这个数前面的数无论是不是逆序都需要进行插入排序操作进行优化 ,所以不用考虑
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 110;
int q[N];
int main()
{ int n ;cin>>n;
for (int i = 1; i <= n; i ++ ) cin>>q[i];
for(int i=n-1;i;i--){
if(q[i]>q[i+1]){
cout<<i;return 0;
}
}
cout << 0;
return 0;
}
拍照https://www.acwing.com/problem/content/1445/
推公式 b[i]=a[i]+a[i+1]
数据范围小 只要确定a1就好办了 所以只要枚举a1
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e3+10;
int n;
int a[N],b[N];
bool vis[N];
bool check(int a1){
memset(vis ,0,sizeof(vis) );
memset(a,0,sizeof(a) );
a[1]=a1;
vis[a1]=true;
for (int i = 2; i <= n; i ++ ){
a[i]=b[i-1]-a[i-1];
if(a[i]<1||a[i]>n) return false;
if( vis[a[i] ]) return false;
vis[a[i]]=true;
}
for (int i = 1; i <= n; i ++ )
cout << a[i]<<" ";
return true;
}
int main()
{
cin>>n;
for(int i=1;i<n;i++) cin>>b[i];
for (int i = 1; i <= n; i ++ )
if(check(i)) break;
return 0;
}
疯狂的科学家https://www.acwing.com/problem/content/1674/
两个只含两种字母的不同的串 每次可以修改将一个区间的字母修改为另一种字母 求修改的次数的最小值
修改只有 两种情况要么修改一次 要么不修改 方法统计修改1次的区间 证明
1.任意包含 交叉情况 都可以转化为 小于等于两个区间
2.经过这种转化后 所需要的情况不会变差
所以可以将 上下不同的串标记为1 相同的串标记0表示不用修改 那么在等价于01串最后只需要求连续的1的区间
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
string a,b;
int main()
{
cin>>n>>a>>b;
int res=0,j=-1;
for (int i = 0; i < n; i ++ ){
if(a[i]!=b[i]) {
j=i+1;
while(j<n&&a[j]!=b[j]) j++;
res++;
i=j;//j加了之后就找到了后一个位置
}
}
cout << res;
return 0;
}
社交距离https://www.acwing.com/problem/content/1662/
编号为 1…N 的奶牛们分别位于一条长直道路上的不同位置(相当于一维数轴),奶牛 i 位于位置 xi。
Farmer John 知道存在一个半径 R,任何与一头被感染的奶牛距离不超过 R 单位的奶牛也会被感染(然后会传染给与其距离 R 单位内的奶牛,以此类推)。
不幸的是,Farmer John 并不确切知道 R 的值。
类似于上面的题 需要找到连续感染的区间的数量 每个区间至少一个牛感染 所以直接输出感染区间的数量
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define x first
#define y second
typedef pair<int,int> PII;
const int N = 1010;
int n;
PII q[N];
int main()
{
cin>>n;
for (int i = 1; i <= n; i ++ ) cin>>q[i].x>>q[i].y;
sort(q+1,q+n+1);
int r=1e8;
for (int i = 1; i < n; i ++ ){
if(q[i].y!=q[i+1].y) r=min(r,q[i+1].x-q[i].x);
}
r=r-1;
int res=0;int j;
for (int i = 1; i <= n; i ++ ){
if(q[i].y) {
j=i+1;
while(j<=n&&q[j].y&&q[j].x-q[j-1].x<=r) j++;//j在范围内 并且第j头牛是感染 且和上一头牛的距离小于r 和上一头牛属于一个区间
res++;//区间数量+1
i=j-1;
}
}
cout << res;
return 0;
}
社交距离 https://www.acwing.com/problem/content/1661/
#include<iostream>
using namespace std;
const int N=100005;
int n,m,ans;
int a[N];
char s[N];
bool check(int x) {
int p=1-x,cnt=0;//cnt不一定恒等于2,小于2代表方案不合理,大于等于2代表方案合理 p=1-x 因为p的含义是上一次插入的牛的位置 p+x才是应该本次牛插入在的位置 要保证(1-x+x=1)+x的位置是比近的(为了应对开头没有牛的情况 此时要放的牛位置为1 而下面的处理没有这种处理 )
for(int i=1; i<=m; i++) {
while(p+x+x<=a[i]) p+=x,cnt++;//如果符合条件,p就往后移动x的距离,牛的头数就加一头
p=a[i];//令p=a[i],p就是前一头牛的位置
}
while(p+x<=n) p+=x,cnt++; //因为存在插入位置没包含最后一个位置n的情况,所以需要继续往后加牛,直到加到不能加位置 处理后面最后一个q[i]位置不是n的情况 因为cnt用完了 p还可以放 所以就继续放
return cnt>=2;//如果加入的牛的数量大于等于2,证明我们的方案是可行的,即x是一种答案
}
int main() {
cin>>n;
cin>>s+1;
for(int i=1; i<=n; i++)
if(s[i]=='1') a[++m]=i;
int l=1,r=n;
for(int i=1; i<m; i++) r=min(r,a[i+1]-a[i]);
while(l<=r) {
int mid=l+r>>1;
if(check(mid)) {
ans=mid;
l=mid+1;
} else r=mid-1;
}
cout<<ans;
return 0;
}
蚂蚁感冒https://www.acwing.com/problem/content/1213/
脑经急转弯
虽然说 调头 但是其实等于穿过!!
假如感冒蚂蚁向右走
那么只有 在这个蚂蚁右边而且向左走的蚂蚁和
在这个蚂蚁左边向右走,而且向左走的蚂蚁 可能被感染!!
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 55;
int n;
int x[N];
int main()
{
cin >> n;
for (int i = 0; i < n; i ++ ) cin >> x[i];
int left = 0, right = 0; // 分别表示左边向右走的蚂蚁数量,和右边向左走的蚂蚁数量
for (int i = 1; i < n; i ++ )
if (abs(x[i]) < abs(x[0]) && x[i] > 0) left ++ ;
else if (abs(x[i]) > abs(x[0]) && x[i] < 0) right ++ ;
if (x[0] > 0 && right == 0 || x[0] < 0 && left == 0) cout << 1 << endl;
else cout << left + right + 1 << endl;
return 0;
}
放养了但没有完全放养https://www.acwing.com/problem/content/3361/
问的是 给定一个排列顺序 如果不是按照这个排列顺序的话 就++
因为唱的是字母歌的话
const int N = 27;
int p[N];
int main()
{
string s;
cin >> s;
for (int i = 0; i < s.size(); i ++ )
p[s[i]-'a' ]=i;//记录每个字母在哪里出现的
cin >> s;
int ans=1;
for (int i = 1; i < s.size(); i ++ )
if( p[s[i]-'a']<=p[s[i-1]-'a' ] ) ans++;
cout << ans;
return 0;
}
叠罗汉的牛
https://www.acwing.com/activity/content/problem/content/1215/
每个牛都有对应的危险值 求最小的值
需要按照 强壮程度+体重排序 把和最小的放最下面
整数拼接
把数组的随机两个数字拼接起来 ai+bi;
要看看对应的a[i]多少个 a[j]*10^len()在里面
a[i]×10^(⌊log10a[j]⌋+1) %k 是小于k的
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N=100010;
int s[11][N];//表示某个数*10^i%k==j的数量
int n;//表示将要输入的n个数
LL a[N];//存放n个数
int k;//表示k倍
LL res;//表示结果
int main()
{
//1.输入数据
cin >> n >> k;
for(int i = 0; i < n; i ++)
cin >> a[i];
//2.预处理s数组
for(int i = 0; i < n; i ++)
{
LL t = a[i] % k;
for(int j = 0; j < 11; j ++)//因为题目中给出的最大数是10^9
{
s[j][t] ++;
t = t * 10 % k;
}
}
//3.循环数组计算答案
for(int i = 0; i < n; i ++)
{
LL t = a[i] % k;
int len = to_string(a[i]).size();//将这个数字转化为字符串,再判断转换后的字符串的位数就等于这个数字本身的位数
res += s[len][(k - t) % k];
// 4.判重
LL r = t;
while(len--) r = r * 10 % k; //等价于求a[i]乘以10^len的余数,同上面的预处理求法一样
if(r == (k - t) % k) res --;
}
//5.输出答案
cout << res << endl;
return 0;
}
杨辉三角https://www.acwing.com/activity/content/problem/content/3899/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n;
LL C(int a, int b){
LL res = 1;
for(int i = a, j = 1; j <= b; i --, j ++){
res = res * i / j;
// 大于n已无意义,且防止爆LL
if(res > n) return res;
}
return res;
}
bool check(int k){
// 二分该斜行,找到大于等于该值的第一个数
// 左边界2k,右边界为max(l, n)取二者最大即可!
int l = 2 * k, r = max(n, l);
while(l < r){
int mid = l + r >> 1;
if(C(mid, k) >= n) r = mid;
else l = mid + 1;
}
if(C(r, k) != n) return false;
// C(r, k)的从0开始的顺序!
cout << 1ll * (r + 1) * r / 2 + k + 1 << endl;
return true;
}
int main(){
cin >> n;
// 从第16斜行枚举即可!
for(int k = 16; ; k --)
if(check(k)) break;
return 0;
}