A - Single Push
背景:
输入描述:
输出描述:
样例:
说明:
题意分析:
题目让我们判断序列A是否满足某个区间[l,r]加上一个非负数k,得到序列B。
我们很自然的想到,观察是否B-A序列的值为至多有一个区间为整数d即可。
代码:
#include<iostream>
#include<stdio.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn],b[maxn];
int main(){
int T;
scanf("%d",&T);
while(T--){
int N;
scanf("%d",&N);
for(int i = 1;i <= N;i++){
scanf("%d",&a[i]);
}
for(int i = 1;i <= N;i++){
scanf("%d",&b[i]);
}
int flag[10] = {0},ans = 1,cnt = 0;
for(int i = 1;i<=N;i++){
if(a[i]-b[i]==flag[cnt])continue;
else {
cnt++;
if(cnt<2||(a[i]-b[i]==0&&cnt==2))flag[cnt] = a[i]-b[i];
else {
ans = 0;
break;
}
}
if(flag[cnt]>0){
ans = 0;
break;
}
}
if(ans)printf("YES\n");
else printf("NO\n") ;
}
return 0;
}
//4
//6
//3 7 1 4 1 2
//3 7 3 6 3 2
//5
//1 1 1 1 1
//1 2 1 3 1
//2
//42 42
//42 42
//1
//7
//6
B - Silly Mistake
背景:
输入描述:
输出描述:
样例:
说明:
题意分析:
题目要我们判断,员工进出公司的序列是否正确。
序列有如下要求
- 员工每天最多只能进入一次办公室。
- 如果那天他之前不进办公室的话,他显然不能离开。
- 每天开始和结束时,办公室都是空的(员工不能呆在晚上)。它也可能在一天中的任何时候都是空的。
答案只需要一个可能的结果,那么我们不妨把一批员工进入到他们全部离算作一天,那么我们使用set来进行进出公司的操作,一个数组存上次员工进入公司的天数来判断进出公司是否合理。
#include<iostream>
#include<stdio.h>
#include<set>
#include<string.h>
using namespace std;
const int maxn = 1e6+5;
int b[maxn];
set<int> S;
set<int>::iterator it;
int r[maxn];
int main(){
int N,a,flag = 1,cnt = 1;
scanf("%d",&N);
S.clear();
memset(b,0,sizeof(b));
for(int i = 1;i <= N;i++){
scanf("%d",&a);
if(a>0){
if(b[a]==cnt){
flag = 0;
break;
}
b[a] = cnt;
S.insert(a);
}
else {
if(b[-a]!=cnt){
flag = 0;
break;
}
it = S.lower_bound(-a);//find()函数不需要 *it==(-a)判断
if(it!=S.end()&&*it==(-a)){
S.erase(*it);
}
else{
flag = 0;
break;
}
}
if(!S.size()){
r[++cnt] = i;
}
}
if(S.size())flag = 0;
if(flag){
printf("%d\n",cnt-1);
for(int i = 2;i <= cnt;i++)
printf("%d ",r[i]-r[i-1]);
}
else printf("-1");
return 0;
}
C - Sweets Eating
背景:
输入描述:
输出描述:
样例:
说明:
题意分析:
给出所有糖的数量N和每天最多吃糖的个数M,输出吃1~N颗糖的最小甜度和,甜度是与吃的天数有关的,第iii天吃点甜度就是i∗wii*w_ii∗wi。
显然越甜的越先吃才行。我们能发现有关规律:
要吃iii颗的情况下被吃的糖,在要吃在i+Mi+Mi+M颗的情况被吃要比吃iii颗的情况下晚一天被吃,
例如:N=9,M=2;
6 19 3 4 4 2 6 7 8;
例如:(第一行天数,第二行糖果甜度对应糖果)
3颗糖:
第一天 | 第二天 |
---|---|
4,3 | 2 |
5颗糖:
第一天 | 第二天 | 第三天 |
---|---|---|
6,4 | 4,3 | 2 |
那么很容易有dp[i]=dp[i−M]+sum[i](sum[i]为前缀和)dp[i] = dp[i-M] +sum[i](sum[i]为前缀和)dp[i]=dp[i−M]+sum[i](sum[i]为前缀和)
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long int ll;
const int maxn = 2e5+5;
int a[maxn];
ll ans[maxn] = {0},sum[maxn];
int main(){
int N,M;
scanf("%d%d",&N,&M);
for(int i = 1;i <= N;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+N);
for(int i = 1;i <= N;i++){
sum[i] = sum[i-1] + a[i];
}
for(int i = 1;i <= N;i++){
ans[i%M]+=sum[i];
printf("%lld ",ans[i%M]);
}
return 0;
}
D - Harmonious Graph
背景:
输入描述:
输出描述:
样例:
说明:
题意分析:
Harmonious Graph :调和图。如果存在u->v(u<v)的路径,那么u和u+1…v-1,v之间都有路径,满足这个条件的就是调和图。
题目给出一系列的点和无向边,求最少加几条边,把图变成调和图。
显然用并查集,但是并查集的合并father节点不任意取,取两待合并的节点的father中最大的节点,方便寻找应该合并为一个并查集的点集的范围,把整个点集内的点合并到一个并查集中花费的边数即是答案第一部分,所有点集处完毕需要的边数的和即是答案。这样处理可以做到线性,否则按照一组组边去处理就会超时。
#include<iostream>
#include<stdio.h>
using namespace std;
const int maxn = 2e5+5;
int pre[maxn];
int find(int x){
return pre[x]==x?x:pre[x] = find(pre[x]);
}
void merge(int x,int y){
int px = find(x),py = find(y);
pre[px] = pre[py] = max(px,py);
}
int main(){
int P,E,str = maxn,end = -maxn;
scanf("%d%d",&P,&E);
for(int i = 1;i<= P;i++)
pre[i] = i;
for(int i = 1;i <= E;i++){
int x,y;
scanf("%d%d",&x,&y);
merge(x,y);
str = min(str,min(x,y));
end = max(end,max(x,y));
}
int now = str,tot = 0;
while(now <= end){
int lM = find(now);
for(int i = now;i<= lM;i++){
int pi = find(i);
if(pi!=lM){
tot++;
merge(lM,pi);
lM = max(lM,find(pi));
}
}
now = lM + 1;
while(pre[now]==now&&now<=end)now++;
}
printf("%d",tot);
return 0;
}
E - Antenna Coverage
背景:
输入描述:
输出描述:
样例:
提示:
题意分析:
要使若干个基站延展信号覆盖整个区域。其中每个基站有用信号覆盖长度,左右区间可以覆盖,增加一个基站的一个单位覆盖需要花费1,请问让整个区域被覆盖要的最小花费。
动态规划,dp表示从(当前位置+1)到终点需要的最小花费。
1)如果pos和pos+1都在当前基站的覆盖区间,那么dp[pos]=dp[pos+1]dp[pos] = dp[pos+1]dp[pos]=dp[pos+1]
2)否则,找到当前点到当前的基站的左端L,求出增加的覆盖L-pos-1,
找到增加覆盖后到达的最右端min(M,L-pos-1+R)
dp[pos]=min(dp[pos],L−pos−1+dp[min(M,L−pos−1+R)]dp[pos]=min(dp[pos],L-pos-1+dp[min(M,L-pos-1+R)]dp[pos]=min(dp[pos],L−pos−1+dp[min(M,L−pos−1+R)]
遍历更新直到到情况1)或者到遍历完
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e2+10;
const int Maxn = 1e5+10;
int l[maxn],r[maxn],dp[Maxn];
int main(){
int N,M;
scanf("%d%d",&N,&M);
for(int i = 1;i<= N;i++){
int x,s;
scanf("%d%d",&x,&s);
l[i] = max(0,x-s);
r[i] = min(M,x+s);
}
dp[M] = 0;
for(int i = M-1;i >=0;i--){
dp[i] = M - i;//最大消费
for(int j = 1;j <= N;j++){
int L = l[j],R = r[j];
if(L<=i+1&&R>=i+1){
dp[i] = dp[i+1];
break;
}
if(L>i){
int lcost = L-i-1;//覆盖到当前位置的花费,也是增加的花费
int cost = min(M,lcost+R);//右边的覆盖
dp[i] = min(dp[i],dp[cost]+lcost);
}
}
}
printf("%d",dp[0]);
}
坑之后填
背景:
输入描述:
输出描述:
样例:
提示: