AtCoder-ABC-178(A-F)
A
输出1-x
B
给定区间[a,b]和区间[c,d],要从区间各取一个数,使得乘积最大。
一开始想复杂了,其实最大值肯定是区间边缘的值相乘得到的,如果包含0,再跟0取个max即可。
C
求长度为n,至少含有一个0和一个9的不同数字序列个数。
利用容斥的思想,不加限制,长度为n的不同数字序列个数为$10^n$,再减去可以含有0,但是不含有9的数字序列个数$9^n$,再同样减去可以含有9,但是不含有0的数字序列个数$9^n$,然后要加上被删除两次的同时不含0和9的序列个数。
D
求每一项大于等于3,且和为给定S的序列个数。
有序的凑硬币问题,基础dp。比如[3,4]和[4,3]是属于不同序列,如果题目要求是属于同一个序列的话,就要将第一重循环改为枚举硬币面额。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
const ll MOD=1e9+7;
ll dp[N];
int s;
int main(){
scanf("%d",&s);
dp[0]=1;
for(int i=1;i<=s;i++){
for(int j=3;j<=i;j++){
if(i-j>=0){
dp[i]=(dp[i]+dp[i-j])%MOD;
}
}
// printf("%d %lld\n",i,dp[i]);
}
printf("%lld\n",dp[s]%MOD);
return 0;
}
E
给定n个点,求两点间最大的曼哈顿距离。
经典题目,做法是将曼哈顿距离公式的绝对值符号拆开,对二维点来说会有4种情况,然后将同一个点的x,y放一起,发现两个点的形式其实是一样的,比如都是$(x_i+y_i)-(x_j+y_j)$,或者是$(-x_i+y_i)-(-x_j+y_j)$,所以直接枚举符号状态,然后遍历一遍求最大最小值。
这个算法也可以改成动态的(支持查询,插入,删除),只需要对每个二进制状态分别维护一个数据结构即可。原题hdu4666
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+50;
int n;
ll x[N],y[N];
int sta[4][2]={{1,1},{1,-1},{-1,1},{-1,-1}};
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x[i],&y[i]);
}
ll ans=0;
for(int i=0;i<4;i++){
ll mx=-1e18;
ll mn=1e18;
for(int j=1;j<=n;j++){
ll t=sta[i][0]*x[j]+sta[i][1]*y[j];
mx=max(mx,t);
mn=min(mn,t);
}
ans=max(ans,mx-mn);
}
printf("%lld\n",ans);
return 0;
}
F
给定a和b长度相同的两个升序数组,要求重排数组b,使得a和b对应位置的数字不同。
a和b都已经升序,所以首先将b逆序,这时候中间就可能存在一段a和b对应位置数字相同,且这段区间都是同一个数字,然后考虑将b数组这些位置的数字交换到其他位置,这里维护一个可以交换的位置下标即可,这样复杂度是O(N)的。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+50;
int n,a[N],b[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[n+1-i]);
}
int idx=1;
bool flag=true;
for(int i=1;i<=n;i++){
if(a[i]==b[i]){
while(idx<=n && (a[i]==b[idx] || a[idx]==b[i])){
idx++;
}
if(idx>n){
flag=false;
break;
}
swap(b[idx],b[i]);
}
}
if(flag){
printf("Yes\n");
for(int i=1;i<=n;i++){
printf("%d%c",b[i],i==n?'\n':' ');
}
}else{
printf("No\n");
}
return 0;
}
目录