10.15
旅行
我对我写的区区一百篇博客每篇都视若己出,只要是我写过的(水题的)题解我是绝不会忘的。
其实我忘了,我只记得这题是个极水的题,我还写了一个40分的假贪心(为什么这么久了还能记得40这个数字?),于是盯着看了几秒在写了几分钟代码就切掉了。
要是我去年noip day1t1也能这么做就好了。
【重庆市NOIP模拟赛】旅行
时限: 1 Sec 内存: 128 MB
提交: 8 解决: 8
题目描述
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 行一个整数,是最大的快乐程度之和所对应的汽艇上的人数(若有多种方案,则输出人数最
多的)。
输入样例
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。
分析
才1000个人啊喂,我们枚举船上坐几个人,然后把每个人此时的心情算出来,然后就取最开心的前这么多人就好了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline void Read(int &p)
{
p=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9')
p=p*10+c-'0',c=getchar();
}
const int MAXN=1024;
struct people
{
int ark,del;
bool operator < (const people &p) const
{
return ark>p.ark;
}
}arr[MAXN];
int n,ans,tmp,pos;
int main()
{
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
Read(n);
for(int i=1;i<=n;i++) Read(arr[i].ark);
for(int i=1;i<=n;i++) Read(arr[i].del);
for(int lim=1;lim<=n;lim++)
{
tmp=0,sort(arr+1,arr+1+n);
for(int i=1;i<=lim;i++) tmp+=arr[i].ark;
if(tmp>=ans) ans=tmp,pos=lim;
for(int i=1;i<=n;i++) arr[i].ark-=arr[i].del;
}
printf("%d\n%d\n",ans,pos);
}
数据
题目描述
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。
输出格式
一个整数,表示把数据修改为合法的情况下,最少需要多少步。
输入样例
4
1 9 3 2
输出样例
2
提示
对于 20%的数据,m<=10, |a[i]|<=5;
对于 60%的数据,m<=5000, |a[i]|<=10000
对于 100%的数据,m<=100000, |a[i]|<=10000
分析
可能我层次比较低,我个人感觉这道题的建图方法是比较,比较巧妙的。我自己居然想到了,这也是比较,比较巧妙的。
第
i
i
i个数,假设它的值为
a
i
a_i
ai,而且
a
i
>
0
a_i\gt 0
ai>0,那么就是说
[
i
+
1
,
i
+
a
i
+
1
]
[i+1,i+a_i+1]
[i+1,i+ai+1]这一段是被它囊括了的,而且不需要调整
a
i
a_i
ai的值,所以我们从
i
i
i到
i
+
a
i
+
1
i+a_i+1
i+ai+1连一条边长为
0
0
0的边,代表我走
0
0
0步就能从
i
i
i走到
i
+
a
i
+
1
i+a_i+1
i+ai+1这个点。
那么我们怎么刻画题目中说的“改正”?我们把
a
i
+
1
a_i+1
ai+1,在图上有什么意义?就是说我们走了这一步以后就走到
i
+
a
i
+
2
i+a_i+2
i+ai+2了,那么我们就从
i
+
a
i
+
1
i+a_i+1
i+ai+1向
i
+
a
i
+
2
i+a_i+2
i+ai+2连一条长度为
1
1
1的边。当然
a
i
−
1
a_i-1
ai−1的边可以由
i
+
a
i
i+a_i
i+ai代劳,而
a
i
+
2
a_i+2
ai+2可以由
i
+
a
i
+
1
→
i
+
a
i
+
2
→
i
+
a
i
+
3
i+a_i+1\to i+a_i+2 \to i+a_i+3
i+ai+1→i+ai+2→i+ai+3这么走。
所以实际操作的时候我们就把每个点和它的下一个点连双向边即可,如果按上面这么整的话可能会有点访问不到;注意还可能会走到外面。
那么答案就是1到n+1的最短路(必须是n+1,如果超出去了你就走回来)。
非常担心边会连太多,于是忧心忡忡地算啊算啊,保守的开了八十万。
于是我点数开小了,RE,只得了85分。
看来是我的境界太低了。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline void Read(int &p)
{
p=0;
int f=1;
char c=getchar();
while(c<'0' || c>'9')
{
if(c=='-') f=-1;
c=getchar();
}
while(c>='0' && c<='9')
p=p*10+c-'0',c=getchar();
p*=f;
}
const int MAXN=1002030,MAXM=802030;
queue<int>q;
bool inq[MAXN];
int n,u,v,mx,arr,ans,cnt,dis[MAXN],head[MAXN],nxt[MAXM],to[MAXM],val[MAXM];
inline void addedge(int u,int v,int w) {nxt[++cnt]=head[u],head[u]=cnt,to[cnt]=v,val[cnt]=w;}
int main()
{
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
Read(n),Read(arr),mx=max(mx,arr+2);
if(arr<0) ans=-arr,addedge(1,2,0);
else addedge(1,arr+2,0);
for(int i=2;i<=n;i++)
{
Read(arr),mx=max(mx,i+arr+1);
if(arr>=0) addedge(i,i+arr+1,0);
addedge(i,i+1,1),addedge(i+1,i,1);
}
for(int i=n+1;i<mx;i++) addedge(i,i+1,1),addedge(i+1,i,1);
memset(dis,0x3f,sizeof dis);
dis[1]=0,q.push(1),inq[1]=1;
while(!q.empty())
{
u=q.front(),inq[u]=0,q.pop();
for(int i=head[u];i;i=nxt[i])
if(dis[to[i]]>dis[u]+val[i])
{
dis[to[i]]=dis[u]+val[i];
if(!inq[to[i]]) q.push(to[i]),inq[to[i]]=1;
}
}
printf("%d\n",ans+dis[n+1]);
}
本题还有DP的做法。
大概就是见下图。
业务
题目描述
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 的最少过路费。
输入样例
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
提示
分析
我的做法(又双叒叕)和std差不多(有时候实现完全不同你也会说是差不多),然而我要回寝室了,所以……。
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
inline void Read(int &p)
{
p=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9')
p=p*10+c-'0',c=getchar();
}
const int MAXN=256;
bool vis[MAXN][2];
int n,m,q,u,v,w,arr[MAXN],G[MAXN][MAXN],ans[MAXN][MAXN],dis[MAXN][2];
struct node
{
int pos,val,is;
node(){}
node(int p,int v,int i){pos=p,val=v,is=i;}
bool operator < (const node &p) const
{
return val>p.val;
}
}hep,to;
priority_queue<node>Q;
inline void del(node p) {if(dis[p.pos][p.is]>=p.val) dis[p.pos][p.is]=p.val,Q.push(p);}
void dijkstra(int sec)
{
memset(vis,0,sizeof vis);
memset(dis,0x3f,sizeof dis);
del(node(sec,0,0));
while(!Q.empty())
{
hep=Q.top(),Q.pop();
if(vis[hep.pos][hep.is]) continue;
vis[hep.pos][hep.is]=1;
for(int i=1;i<=n;i++)
if(G[hep.pos][i]!=0x3f3f3f3f)
{
to=node(i,dis[hep.pos][hep.is]+G[hep.pos][i],hep.is);
if(arr[i]>arr[sec] && !hep.is) to.is=1,del(to);
if(arr[i]==arr[sec]) {del(to); if(i!=sec) to.is=1,del(to);}
if(arr[i]<arr[sec]) del(to);
}
}
}
int main()
{
freopen("business.in","r",stdin);
freopen("business.out","w",stdout);
memset(G,0x3f,sizeof G),memset(ans,0x3f,sizeof ans);
Read(n),Read(m),Read(q);
for(int i=1;i<=n;i++) Read(arr[i]);
for(int i=1;i<=m;i++) Read(u),Read(v),Read(w),G[u][v]=min(G[u][v],w),G[v][u]=min(G[v][u],w);
for(int sec=1;sec<=n;sec++)
{
dijkstra(sec);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans[i][j]=min(ans[i][j],min(dis[i][0]+dis[j][1],dis[i][1]+dis[j][0])+arr[sec]);
}
while(q--) Read(u),Read(v),printf("%d\n",ans[u][v]);
}