比賽題很水呀
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,唉。