您的位置:首页 > 其它

20170724考试总结

2017-07-24 16:13 218 查看
第一题:旅行


题目描述

Mr_H 旗下的 n 个 OIer 坐船外出旅行!

但是他们只有一艘船,虽然船能装下全部的 Oier,但太拥挤将会影响众 OIer 的心情,所以 Mr_H

决定选择一部分 Oier 去。我们假设,每个人单独坐船的快乐程度是 Ci,而船上每多一个人,他的

快乐程度会减去 Di。

现在你的任务是帮助 Mr_H 计算,选择那些人,才能使船上所有人的快乐程度之和达到最大。


输入

第 1 行是一个整数 n,表示 OIer 的人数;

第 2 行有 n 个整数,第 i 个整数表示第 i 个人人单独坐船的快乐程度 Ci(1<=Ci<=10000);

第 3 行有 n 个整数,第 i 个整数表示每多 1 人,第 i 个人快乐程度的下降值 Di(1<=Di<=10)。


输出

第 1 行一个整数,是最大的快乐程度之和;

第 2 行一个整数,是最大的快乐程度之和所对应的汽艇上的人数(若有多种方案,则输出人数最

多的)。


样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
6
10 10 10 10 10 9
2 2 2 2 2 3


样例输出

18
3


提示

前 3 个人去坐汽艇可使快乐程度之和达到最大,每个人的快乐程度均为 10-2*2=6,总和是 18。

对于 30%的数据,n<=20;

对于 100%的数据,n<=1000。

题解:枚举上船的人数(n),然后把所有人按Ci-(n-1)*Di排序,取前n个。P.s:这道题不能用dp,因为假设前i人中选j人的状态中,不选i最优,但如果选i可能当前不最优但sumD较小,以后可能是最优解的一部分,而如果用dp,选i的情况因为当前不最优就会被刷掉而后面无法用他更新其他状态。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000+10;
int n,now,res,ans,num;
struct node{
int c,d;
bool operator < (const node x)const{
return c-(now-1)*d>x.c-(now-1)*x.d;
}
}arr
;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&arr[i].c);
for(int i=1;i<=n;i++)
scanf("%d",&arr[i].d);
for(now=1;now<=n;now++){
sort(arr+1,arr+n+1),res=0;
for(int i=1;i<=now;i++)
res+=arr[i].c-(now-1)*arr[i].d;
if(res>=ans)
ans=res,num=now;
}
printf("%d\n%d\n",ans,num);
}
/*
6 10 10 10 10 10 9 2 2 2 2 2 3
*/
成绩:AC

分析:刚开始也发现人数如果不确定无法贪心时,就考虑过dp,然而幸好一看第二题明显dp(一套模拟赛怎么可能两道dp o(╯□╰)o)就马上转换了思路枚举人数~\(≧▽≦)/~啦啦啦。

第二题:数据


题目描述

Mr_H 出了一道信息学竞赛题,就是给 n 个数排序。输入格式是这样的:

试题有若干组数据。每组数据的第一个是一个整数 n,表示总共有 n 个数待排序;接下来 n 个

整数,分别表示这 n 个待排序的数。

例如:3 4 2 –1 4 1 2 3 4,就表示有两组数据。第一组有 3 个数(4,2,-1),第二组有 4

个数(1,2,3,4)。可是现在 Mr_H 做的输入数据出了一些问题。例如:2 1 9 3 2 按理说第一组数

据有 2 个数(1,9),第二组数据有 3 个数,可是“3”后面并没有出现三个数,只出现了一个数“2”

而已!

现在 Mr_H 需要对数据进行修改,改动中“一步”的含义是对文件中的某一个数+1 或-1,写个

程序,计算最少需要多少步才能将数据改得合法。


输入

第一行一个整数 m,表示 Mr_H 做的输入数据包含的整数个数。第二行包含 m 个整数 a[i],每

个整数的绝对值不超过 10000。


输出

一个整数,表示把数据修改为合法的情况下,最少需要多少步。


样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
4
1 9 3 2


样例输出

2


提示

对于 20%的数据,m<=10, |a[i]|<=5;

对于 60%的数据,m<=5000, |a[i]|<=10000

对于 100%的数据,m<=100000, |a[i]|<=10000

题解:这是一道很明显的dp,方程式也很水,dp[i]表示前i个元素满足条件的最少操作次数。dp[i]=dp[j]+abs(a[j+1]-(i-j-1));但如果直接朴素地dp时间是O(n^2)最多拿60分,所以考虑优化,首先把绝对值拆开。

dp[i]=dp[j]-a[j+1]+i-j-1 (i-j-1>=a[j+1])

dp[i]=dp[j]+a[j+1]-i+j+1 (i-j-1<=a[i+1])

因为a[i]的值并不单调,所以这里不能使用单调队列优化(无法保证i-j-1和a[j+1]的大小关系),于是可以选择用线段树。以dp[i]=dp[j]-a[j+1]+i-j-1 (i-j-1>=a[j+1])为例,i-j-1>=a[j+1] => i-1>=a[j+1]+j 所以以离散化后的a[j+1]+j作为线段树的下标,又因为dp[j]-a[j+1]-j与i无关所以用线段树维护这一部分的min。另一种情况也一样。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
#define lson num<<1
#define rson num<<1|1
using namespace std;
const int N=100000+10;
const int T=1000000+10;
const int inf=0x3f3f3f3f;
struct node{
int l,r,Min;
}tree[N<<4][2];
int n,arr
,h
,dp
,tmp[2];
void update(int num,bool flag){
tree[num][flag].Min=min(tree[lson][flag].Min,tree[rson][flag].Min);
}
void build(int num,int l,int r,bool flag){
tree[num][flag].l=l;
tree[num][flag].r=r;
tree[num][flag].Min=inf;
if(l==r) return ;
int mid=(l+r)>>1;
build(lson,l,mid,flag);
build(rson,mid+1,r,flag);
}
void insert(int num,int pos,int val,bool flag){
if(tree[num][flag].l==pos&&tree[num][flag].r==pos)
tree[num][flag].Min=min(tree[num][flag].Min,val);
else{
int mid=(tree[num][flag].l+tree[num][flag].r)>>1;
if(pos<=mid) insert(lson,pos,val,flag);
else	insert(rson,pos,val,flag);
update(num,flag);
}
}
void getmin(int num,int l,int r,bool flag){
if(tree[num][flag].l>r||tree[num][flag].r<l)
return ;
else if(tree[num][flag].l>=l&&tree[num][flag].r<=r)
tmp[flag]=min(tmp[flag],tree[num][flag].Min);
else getmin(lson,l,r,flag),getmin(rson,l,r,flag);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
h[i]=arr[i]+i-1;
}
sort(h+1,h+n+1);
int siz=unique(h+1,h+n+1)-h-1,pos;
build(1,1,siz,0),build(1,1,siz,1);
dp[0]=0;
pos=lower_bound(h+1,h+siz+1,arr[1])-h;
insert(1,pos,dp[0]-arr[1],0);
insert(1,pos,dp[0]+arr[1],1);

dp[1]=abs(arr[1]);
pos=lower_bound(h+1,h+siz+1,arr[2]+1)-h;
insert(1,pos,dp[1]-arr[2]-1,0);
insert(1,pos,dp[1]+arr[2]+1,1);

dp[2]=abs(arr[1]-1);
pos=lower_bound(h+1,h+siz+1,arr[3]+2)-h;
insert(1,pos,dp[2]-arr[3]-2,0);
insert(1,pos,dp[2]+arr[3]+2,1);

for(int i=3;i<=n;i++){
tmp[0]=tmp[1]=inf;
pos=lower_bound(h+1,h+siz+1,i-1)-h;
int p=pos;
if(h[p]>i-1) p--;
getmin(1,pos,siz,1);
getmin(1,0,p,0);
dp[i]=min(tmp[0]+i-1,tmp[1]-i+1);
pos=lower_bound(h+1,h+siz+1,arr[i+1]+i)-h;
insert(1,pos,dp[i]-arr[i+1]-i,0);
insert(1,pos,dp[i]+arr[i+1]+i,1);
}
printf("%d\n",dp
);
}


dp[i]=dp[j]-a[j+1]+i-j-1 (i-j-1>=a[j+1])

dp[i]=dp[j]+a[j+1]-i+j+1 (i-j-1<=a[i+1])

还可以用优先队列对dp进行优化,用两个优先队列,因为当a[j+1]+j+1<=i满足时随着i的增加后面的情况当前j也一定满足,所以开一个优先队列维护最小a[j+1]+j+1当堆顶小于i时弹出堆顶,用一个变量Min记录这样弹出的j中最小的dp[j]-a[j+1]-j-1。另一个优先队列维护dp[i]+arr[i+1]+i+1的最小值,必须满足a[j+1]+j+1>=i,若不满足直接弹出(i递增当前不满足,以后一定不可能再满足)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=100000+10;
const int inf=0x3f3f3f3f;
struct node{
int val,pos;
node(){}
node(int a,int b){
val=a,pos=b;
}
bool operator < (const node &x)const{
return val>x.val;
}
};
priority_queue<node> q;
priority_queue<node> Q;
int n,arr
,dp
;
int cal(int i,bool flag){
if(!flag) return dp[i]-arr[i+1]-i-1;
else return dp[i]+arr[i+1]+i+1;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&arr[i]);
q.push(node(arr[1]+0+1,0));
Q.push(node(cal(0,1),arr[1]+1));
int Min=inf;
for(int i=1;i<=n;i++){
while(!q.empty()&&q.top().val<=i)
Min=min(Min,cal(q.top().pos,0)),q.pop();
dp[i]=Min+i;
while(!Q.empty()&&Q.top().pos<i) Q.pop();
if(!Q.empty())
dp[i]=min(dp[i],Q.top().val-i);
q.push(node(arr[i+1]+i+1,i));
Q.push(node(cal(i,1),arr[i+1]+i+1));
}
printf("%d\n",dp
);
}

其实还有更简单的方法,只用一个优先队列,同线段树下标,pos[i]=a[i+1]+i+1,那么dp方程式可以变形:

dp[i]=dp[j]+abs(pos[j]-i) 

=> pos[j]>i:
dp[i]=dp[j]+pos[j]-i

=> pos[j]<=i: dp[i]=dp[j]+pos[j]-2*pos[j]+i;

优先队列弹出堆顶,判断pos[j]与i的关系,然后转移。o(╯□╰)o

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=100000+10;
const int inf=0x3f3f3f3f;
struct node{
int val,pos;
node(){}
node(int a,int b){
val=a,pos=b;
}
bool operator < (const node &x)const{
return val>x.val;
}
};
priority_queue<node> q;
int n,arr
,pos
,dp
;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&arr[i]);
for(int i=0;i<=n;i++)
pos[i]=arr[i+1]+i+1;
q.push(node(dp[0]+pos[0],pos[0]));
int Min=inf;
for(int i=1;i<=n;i++){
while(!q.empty()&&q.top().pos<i)
Min=min(Min,q.top().val-2*q.top().pos),q.pop();
dp[i]=Min+i;
if(!q.empty())
dp[i]=min(dp[i],q.top().val-i);
q.push(node(dp[i]+pos[i],pos[i]));
}
printf("%d\n",dp
);
}


成绩:75 /(ㄒoㄒ)/~~

分析:考试时已经码出了100分的代码,因为担心出错又写了一个暴力n*n<=1000000时就跑暴力,结果忘了乘1LL,n*n爆成了负数,导致大数据莫名跑了暴力O__O"…


第三题:业务


题目描述

Mr_H 谋得一份兼职——货车司机,从此以后他将会开着货车穿行在 C 国的各大城市之间。

C 国中有 n 座城市(编号为 1~n),并且有 m 条双向公路,每条公路连接两座不同的城市。货车

从任意一座城市出发都可以抵达任意另一座城市。在每条公路上,都有一个收费站,通过的车辆需要

交纳一定过路费。可能有多条公路连接相同的两座城市。

为了增加财政收入,C 国还在每座城市也设置了收费站。并且规定,车辆从一座城市到另一座城

市的费用是,所经过公路费用和,加上所经过的城市中费用的次大值

...(这里的次大可以和最大相同,

但是城市不同)。

(这里的次大可以和最大相同,

但是城市不同)。

现在 Mr_H 告诉你今年 k 次业务运送货物的起点、终点城市列表,请你帮忙计算,每次业务需要

交纳的最低过路费。


输入

第一行包含三个用一个空格隔开的整数:n,m,k。其意义如题目描述。

第 2 到第 n+1 行:第 i+1 行包含一个单独的整数 c(1<=c<=100000),表示城市 i 的费用。

接下来的 m 行,每行包含三个整数 a,b,w,表示一条公路连接城市 a 和城市 b(1<=a,b<=n),

其过路费为 w(1<=w<=100000)。

最后的 k 行,每行包含两个整数:s,t,表示一次业务的起点和终点(1<=s,t<=n 且 s!=t)。


输出

共 k 行,每行一个整数,表示从城市 s 到 t 的最少过路费。


样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
5 7 3
2
5
3
3
4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 3
1 4
2 3


样例输出

4
7
8


提示



题解:比较类似Floyd的思想,枚举i到j中第二大的城市k,从k开始跑spfa,dis[i][1]表示k到i经过的最大城市为k时的最小dis,dis[i][0]表示k到i经过的第二大城市为k时的最小dis。dis[i][0]=min(dis[j][1]+map[j][1])(c[j]>c[k])
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=250+10;
int T,n,m,s,t,w,now,c
;
int dp

,map

,dis
[2];
queue<int> q;
bool vis
;
void spfa(int start){
memset(dis,0x3f,sizeof dis);
dis[start][0]=dis[start][1]=0;
memset(vis,0,sizeof vis);
while(!q.empty()) q.pop();
q.push(start);
while(!q.empty()){
vis[now=q.front()]=0,q.pop();
for(int i=1;i<=n;i++)if(map[now][i]){
if(c[i]<=c[start]){
if(dis[i][0]>dis[now][0]+map[now][i]){
dis[i][0]=dis[now][0]+map[now][i];
if(!vis[i]) vis[i]=1,q.push(i);
}
if(dis[i][1]>dis[now][1]+map[now][i]){
dis[i][1]=dis[now][1]+map[now][i];
if(!vis[i]) vis[i]=1,q.push(i);
}
}
else if(dis[i][0]>dis[now][1]+map[now][i]){
dis[i][0]=dis[now][1]+map[now][i];
if(!vis[i]) vis[i]=1,q.push(i);
}
}
}
}
int main(){
scanf("%d %d %d",&n,&m,&T);
memset(map,0,sizeof map);
ab2e

for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
while(m--){
scanf("%d %d %d",&s,&t,&w);
if(!map[s][t]) map[s][t]=map[t][s]=w;
else map[s][t]=map[t][s]=min(map[s][t],w);
}
memset(dp,0x3f,sizeof dp);
for(int k=1;k<=n;k++){
spfa(k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=min(dp[i][j],min(dis[i][0]+dis[j][1],dis[i][1]+dis[j][0])+c[k]);
}
while(T--){
scanf("%d %d",&s,&t);
printf("%d\n",dp[s][t]);
}
}


成绩:16   o(>﹏<)o
分析:感觉自己还是太单纯了%>_<%,直接跑spfa记录路径,然后wa掉(当前Ans最小时可能dis较大不能用来更新其他Ans)。

总结:主要还是第二题的线段树在思路还不够清晰时就开始写以至于调试了很久,第三题几乎没有什么时间细想,当时做第二题时还是应该想清楚再开始,说不定能节省更多的时间,第三题也不应该那么慌,冷静一点原本思路的漏洞还是很明显的。o(╯□╰)o。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: