比赛题很水呀
T1.同行
题目描述
给定一个 N 个点 M 条边的无向图,其中 Bessie 在 1 号点,Elsie 在 2 号点,
它们的目的地为 N 号点。Bessie 每经过一条边需要消耗 B 点能量,Elsie 每经过一条
边需要消耗 E 点能量。当它们相遇时,它们可以一起行走,此时它们每经过一条边需要消
耗 P 点能量。求它们两个到达 N 号点时最少消耗多少能量?
输入
第一行,五个整数 B, E, P, N,M (B,E,P<=40,000)
下面 m 行每行两个数 u,v 表示一条无向边(u,v)。 (1<=u,v<=n)
输出
一个整数,表示最小能量消耗。。
不说了,水的一匹。建无向图然后跑三个最短路即可。
代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#define LL long long
using namespace std;
const LL maxn=40010,inf=1e18;
vector <pair<LL,LL> > ma1[maxn],ma2[maxn],fanma[maxn];
priority_queue <pair<LL,LL> > heap;
LL dis1[maxn],dis2[maxn],disn[maxn],n,m;
void dij(LL x,vector <pair<LL,LL> > *ma,LL *dis){
LL i,w;vector <pair<LL,LL> > ::iterator it;
for(i=1;i<=n;i++)dis[i]=inf;
while(!heap.empty())heap.pop();
dis[x]=0;heap.push(make_pair(0,x));
while(!heap.empty()){
w=-heap.top().first;x=heap.top().second;heap.pop();
if(w!=dis[x])continue;
for(it=ma[x].begin();it!=ma[x].end();it++)if(dis[x]+it->first<dis[it->second]){
dis[it->second]=dis[x]+it->first;
heap.push(make_pair(-dis[it->second],it->second));
}
}
}
int main(){
//freopen("walk.in","r",stdin);
//freopen("walk.out","w",stdout);
LL i,j,b,e,p,x,y,ans=inf;
scanf("%lld%lld%lld%lld%lld",&b,&e,&p,&n,&m);
for(i=1;i<=m;i++){
scanf("%lld%lld",&x,&y);
ma1[x].push_back(make_pair(b,y));
ma1[y].push_back(make_pair(b,x));
ma2[y].push_back(make_pair(e,x));
ma2[x].push_back(make_pair(e,y));
fanma[y].push_back(make_pair(p,x));
fanma[x].push_back(make_pair(p,y));//存边
}
dij(1,ma1,dis1);
dij(2,ma2,dis2);
dij(n,fanma,disn);//三次dijstar()
for(i=1;i<=n;i++)ans=min(ans,dis1[i]+dis2[i]+disn[i]);
printf("%lld",ans);
return 0;
}
T2.长跑
问题描述
在二维平面上有 N 个点,从(x1,y1)到(x2,y2)的代价为|x1-x2|+|y1-y2|。
求从 1 号点出发,按从 1 到 N 的顺序依次到达每个点的最小总代价。
你有 K 次机会可以跳过某个点,不允许跳过 1 号点或 N 号点。。
输入
第一行 N,K (2<=N<=500,0<=K<=N-2)
接下来 N 行每行两个数(x,y)表示第 i 个点的座标。
(-1000 <= x <= 1000, -1000 <= y <= 1000)
输出
一行,1 整实数,表示最小代价。
水水水题。 dp暴力乱搞即可。以为要爆(开玩笑一个小目标的复杂度),还想了很多优化,最后还是把动归写起。
状态: 表示跑到第i点跳了j次使用的最小代价。
方程: 注意
根本没有优化的余地了。
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;
const LL maxn=510,inf=1e18;
LL f[maxn][maxn],x[maxn],y[maxn],n;
LL getdis(LL a,LL b){
return abs(x[a]-x[b])+abs(y[a]-y[b]);
}
int main(){
//freopen("run.in","r",stdin);
//freopen("run.out","w",stdout);
LL i,j,k,o;
scanf("%lld%lld",&n,&k);
for(i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
memset(f,3,sizeof(f));
f[1][0]=0;
for(i=2;i<=n;i++)
for(j=1;j<i;j++){
for(o=0;o+i-j-1<=k;o++){
f[i][o+i-j-1]=min(f[i][o+i-j-1],f[j][o]+getdis(i,j));
}
}//动归部分
printf("%lld",f[n][k]);
return 0;
}
T3.糖果
问题描述
幼儿园的小孩们收到了一个有 M 颗糖果的大包裹,现在要把这些糖果分给 N
个小孩。 每一个小孩都给出了一个期望的糖果数,如果没有达到他的期望值
a[i],小孩就会生气。每差一个糖果,小孩的生气指数就会增加。他生气的程
度等于他少得到的糖果数的平方。比如,Mirko 想要得到 32 个糖果,但是只得
到了 29 个。他少了 3 个,所以他的生气指数是 9。
不幸的是,糖果数不足以满足所有小孩的期望。所以我们应该采取最优的分
配方法,使得最后小孩们的生气指数之和最小。
输入
第 1 行:2 个整数 M,N
第 2..N+1 行:第 i+1 行表示第 i 个小朋友期望值 a[i]。
输出
第 1 行:1 个整数,表示最小的生气指数总和。
可以转化为另一个问题:将一个数分成 个数使它们的平方和尽量小即可。用柯西不等式证明得最小时所有数均相同(而且这很显然吧)
这时我们算出有的糖果与需要得糖果的差值记为 。将 首先平均分配最优地在每个小朋友身上(注意必须是整数),即:
完了?没有。我们考虑到有的小朋友需要的糖果数小于平均数,而我们不可能使他得到的糖果数为负,所以我们还要将原来的 排序后一个个扫,若存在需要数比平均数小则更新答案和平均数,最后再将答案加上 式即可。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const LL maxn=100010;
LL sum=0,n,m,a[maxn];
int main(){
freopen("candy.in","r",stdin);
freopen("candy.out","w",stdout);
LL ping,sheng,i,j,t,del,ans=0,cnt;
scanf("%lld%lld",&m,&n);
for(i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum+=a[i];
}
sort(a+1,a+n+1);
del=sum-m;
if(del<=0){
printf("0");return 0;
}
ping=del/n;
i=1;
cnt=n;
while(a[i]<ping){
ans+=a[i]*a[i];
cnt--;del-=a[i];
ping=del/cnt;
i++;
}//处理比平均数小的小朋友的情况
sheng=del-ping*cnt;
printf("%lld",sheng*(ping+1)*(ping+1)+(cnt-sheng)*ping*ping+ans);
return 0;
}
T4.抗议
问题描述
约翰家的 N 头奶牛正在排队游行抗议。一些奶牛情绪激动,约翰测算下来,
排在第 i 位的奶牛的理智度为 Ai,数字可正可负。
约翰希望奶牛在抗议时保持理性,为此,他打算将这条队伍分割成几个小组,
每个抗议小组的理智度之和必须大于或等于零。奶牛的队伍已经固定了前后顺
序,所以不能交换它们的位置,所以分在一个小组里的奶牛必须是连续位置的。
除此之外,分组多少组,每组分多少奶牛,都没有限制。
约翰想知道有多少种分组的方案,由于答案可能很大,只要输出答案除以
1000000009 的余数即可。
输入格式
第一行:单个整数 N
接下来 N 行,每行有一个整数 Ai。
输出格式
一个整数:表示分组方案数模 1000000009 的余数
数据范围
对于 50% 的数据,1<= N <=200
对于 100%的数据,1 ≤ N ≤ 105 −105 ≤ Ai ≤ 105
很好的一道动归递推套树状数组的题。
状态: 表示前 头奶牛能有的方案总数。
方程: 其中 表示奶牛理智值的前缀和。
时间复杂度:
显然超时,但考虑条件限制中可变成 可用树状数组维护前 的 和(类似于逆序对的样子)。
将前缀和排序后编号维护树状数组即可。
时间复杂度:
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const LL mod=1000000009,maxn=100010;
LL tree[maxn],f[maxn],a[maxn],c[maxn],k[maxn],n;
LL lowbit(LL x){
return x&(-x);
}
void modify(LL x,LL del){
for(;x<=n+1;x+=lowbit(x))tree[x]=(tree[x]+del)%mod;
}
LL getsum(LL x){
LL sum=0;
for(;x>0;x-=lowbit(x))
sum=(sum+tree[x])%mod;
return sum;
}
struct node{
LL x,w;
}p[maxn];
bool cmp(node a,node b){
return a.w<b.w;
}
int main(){
//freopen("protest.in","r",stdin);
//freopen("protest.out","w",stdout);
LL i,j;
scanf("%lld",&n);
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
for(i=1;i<=n;i++){
p[i].w=p[i-1].w+a[i];
p[i].x=i;
}
sort(p,p+n+1,cmp);
for(i=0;i<=n;i++){
if(i&&p[i].w==p[i-1].w)k[i]=k[i-1];
else k[i]=i+1;
c[p[i].x]=k[i];//离散化编号
}
modify(c[0],1);
for(i=1;i<=n;i++){
f[i]=getsum(c[i]);
modify(c[i],f[i]);
}
printf("%lld",f[n]);
return 0;
}
总结与反思:
需要改进的:
T1:考试时没写vector指针,代码很冗杂,现在学会了。
T2:最开始不都不敢写暴力dp,还去想优化。暴力出奇迹,必须用暴力呀。
T4:一定要检查自己是否忘记取余了!!!!!并且树状数组要避免负数与0。博主现在都没搞懂自己之前代码哪里错了。差点AK,唉。