day2018.10.21模擬賽總結
今天是NOIP考前模擬賽day3了.
發揮得海星,自我感覺良好.
T1:
題目大意:給定三種氣球的數量a,b,c,兩種或三種氣球可以3個組成一組,求最多能分多少組.
考場得分:100.
顯然我們設兩種氣球的數量為x,y,當且時,這兩種氣球的最多組數就是.
那麼我們將讀入進來的三個數排序之後存入a,b,c.當a為0時,我們發現答案就是;不為0時分兩種情況,一種是,那麼答案就為a+b;另一種是,那麼答案就為.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void typedef long long LL; LL a[5],ans; Abigail into(){ scanf("%lld%lld%lld\n",&a[1],&a[2],&a[3]); } Abigail work(){ sort(a+1,a+4); if (a[1]==0){ if (a[2]==0) ans=0; else if (a[2]*2>=a[3]) ans=(a[2]+a[3])/3; else ans=a[2]; }else{ if (a[1]==a[2]&&a[2]==a[3]) ans=a[1]; else if ((a[1]+a[2])*2<=a[3]) ans=a[1]+a[2]; else ans=(a[1]+a[2]+a[3])/3; } } Abigail outo(){ printf("%lld\n",ans); } int main(){ freopen("decorate.in","r",stdin); freopen("decorate.out","w",stdout); int T; scanf("%d",&T); while (T--){ into(); work(); outo(); } return 0; }
實際上可以處理的更加簡潔一些,我是由於考場上想出來之後不願意花太多時間化簡了,就直接寫出來了.
T2:
題目大意:給定一大堆長度不超過10的字串作為節點編號,給出從每個點開始可以直接走到哪些點,求是否有環.
考場得分:100.
看起來十分水的一道題,用一個拓撲排序判環即可解決這個問題.
但是這道題大概考的不是如何判環,考的是如何輸入字串——首先,字串讀入沒有一行有多少個數的資訊,只能靠getline;其次,還得考慮讀入一行的字串後如何分離;最後,還有一個將每一個字串對應一個離散過後的點的過程.
所以我們用getline讀入每一行,然後暴力分離每一個字串,將其按照37進位制數對應一個long long的數(不直接塞map裡是怕超時),然後塞進map裡對應一個數即可完成建圖過程.
建完圖之後就直接可以開始拓撲排序判環啦>_<.
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define Abigail inline void const int N=450000; typedef long long LL; map<LL,int> mmap; int n,splay,top; struct side{ int y,next; }e[N+9]; int lin[N+9],deg[N+9]; string s; struct Que{ int q[N+9],h,t; Que(){h=1;t=0;} bool empty(){return h>t;} int front(){return q[h];} void push(int x){q[++t]=x;} void pop(){++h;} }q; LL strLL(int st,int en){ LL x=0; for (int i=st;i<=en;i++) if (s[i]<='9'&&s[i]>='0') x=x*37+s[i]-'0'+1; else x=x*37+s[i]-'A'+11; return x; } void ins(int x,int y){ e[++top].y=y; e[top].next=lin[x]; lin[x]=top; } void topsort(){ Que(); for (int i=1;i<=splay;i++) if (!deg[i]) q.push(i); while (!q.empty()){ int t=q.front();q.pop(); for (int i=lin[t];i;i=e[i].next){ --deg[e[i].y]; if (deg[e[i].y]) continue; q.push(e[i].y); } } } bool check(){ for (int i=1;i<=splay;i++) if (deg[i]) return true; return false; } Abigail into(){ int last; mmap.clear(); scanf("%d",&n);getline(cin,s); for (int i=1;i<=n;i++){ getline(cin,s); int j=s.size()-1; if (s[j]<='9'&&s[j]>='0'||s[j]<='Z'&&s[j]>='A') s=s+" "; last=j=0; LL x,y; for (;j<s.size();j++) if (s[j]<='9'&&s[j]>='0'||s[j]<='Z'&&s[j]>='A') break; for (;j<s.size();j++) if ((s[j]>'9'||s[j]<'0')&&(s[j]>'Z'||s[j]<'A')){ x=strLL(last,j-1); if (mmap.find(x)==mmap.end()) mmap.insert(make_pair(x,++splay)); last=++j; break; } for (;j<s.size();j++) if ((s[j]>'9'||s[j]<'0')&&(s[j]>'Z'||s[j]<'A')){ y=strLL(last,j-1); if (mmap.find(y)==mmap.end()) mmap.insert(make_pair(y,++splay)); ins(mmap[x],mmap[y]); deg[mmap[y]]++; last=j+1; } } } Abigail work(){ topsort(); } Abigail outo(){ if (check()) puts("Yes"); else puts("No"); for (int i=1;i<=splay;i++) deg[i]=0,lin[i]=0; for (int i=1;i<=top;i++) e[i].y=e[i].next=0; splay=0;top=0; } int main(){ freopen("dependency.in","r",stdin); freopen("dependency.out","w",stdout); int T; scanf("%d",&T); while (T--){ into(); work(); outo(); } return 0; }
T3:
題目大意:給定一張強連通圖,讓你將點1~b分為s組,使得每一組中每一對點的距離和最小,其中這裡的距離是指兩個點x,y,x到b+1的距離b+1到y的距離.
考場得分:0 / 65分(NOI Linux / 校內OJ).
爆0的原因是MLE了,校內OJ不知為何沒有MLE...
考場上我的思路就是發現我們肯定要先跑最短路,但是由於時間不夠了我寫了個floyd.
那麼我們很容易想到一個貪心就是距離b+1越小的點越優秀,所以我們肯定讓距離b+1越小的點分配在一個任務裡的數量越多,那麼我們就可以搞出一個十分直接的貪心.
但是貪心的錯誤率比較高,所以我們先按照到b+1的距離從小到大排序,然後用一個DP,用f[i][j]表示i個點分配j個任務的最小距離和,那麼就可以列出方程:
其中sum[i][j]表示排序後在區間[i,j]裡的點分配在一個任務裡的貢獻,可以預處理出.
我們發現這個演算法十分優秀,時間複雜度為,能夠拿到65分的高分,但是我因為開了三個的long long陣列而MLE了.
考場MLE程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=5000;
const LL INF=(1LL<<60LL)-1LL;
int n,m,b,s;
LL dis[N+9][N+9],sum[N+9][N+9],f[N+9][N+9];
struct node{
int x,v;
bool operator < (const node &p)const{return v<p.v;}
}ord[N+9];
void floyd(){
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
Abigail into(){
scanf("%d%d%d%d",&n,&b,&s,&m);
int x,y;LL l;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
dis[i][j]=INF;
for (int i=1;i<=m;i++){
scanf("%d%d%lld",&x,&y,&l);
dis[x][y]=min(dis[x][y],l);
}
}
Abigail work(){
floyd();
for (int i=1;i<=b;i++)
ord[i].x=i,ord[i].v=dis[i][b+1]+dis[b+1][i];
sort(ord+1,ord+1+b);
for (int len=1;len<=n;len++)
for (int i=1;i+len-1<=n;i++){
sum[i][i+len-1]=sum[i][i+len-2];
for (int j=i;j<=i+len-2;j++)
sum[i][i+len-1]+=dis[ord[j].x][b+1]+dis[b+1][ord[i+len-1].x]+dis[ord[i+len-1].x][b+1]+dis[b+1][ord[j].x];
}
for (int i=0;i<=n;i++)
for (int j=0;j<=s;j++)
f[i][j]=INF;
f[0][0]=0;
for (int i=1;i<=b;i++)
for (int j=1;j<=s;j++)
for (int k=0;k<i;k++)
f[i][j]=min(f[i][j],f[k][j-1]+sum[k+1][i]);
}
Abigail outo(){
printf("%lld\n",f[b][s]);
}
int main(){
freopen("assignment.in","r",stdin);
freopen("assignment.out","w",stdout);
into();
work();
outo();
return 0;
}
正解是在上面這個樸素DP的基礎上,進行一個神奇的優化.
顯然,這個DP的轉移過程中,一個區間的大小肯定大於後面的區間的大小,所以上述轉移中的k一定滿足.
所以時間複雜度為,由於調和級數約等於log,所以時間複雜度為,由於實現較為寬鬆且調和級數跑不滿,所以可以AC.
然後將floyd換成兩邊dijkstra,sum陣列換成使用一組內距離之和*(元素個數-1)即可.
程式碼如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=5000,M=50000,inf=(1<<30)-1;
const LL INF=(1LL<<60LL)-1LL;
int n,m,b,s;
LL f[2][N+9],sum[N+9],now=0,old=1;
struct side{
int y,next,v;
}e[M*2+9];
int lin[N+9][N+9],dis[2][N+9],top,use[N+9];
struct state{
int x,v;
state(){x=v=0;}
state(int xx,int vv){x=xx;v=vv;}
bool operator > (const state &p)const{return v>p.v;}
bool operator < (const state &p)const{return v<p.v;}
}ord[N+9];
priority_queue<state,vector<state>,greater<state> >qmin;
void ins(int t,int x,int y,int v){
e[++top].y=y;e[top].v=v;
e[top].next=lin[t][x];
lin[t][x]=top;
}
void dijkstra(int t,int st){
for (int i=1;i<=n;i++) dis[t][i]=inf,use[i]=0;
dis[t][st]=0;qmin.push(state(st,0));
while (!qmin.empty()){
int tt=qmin.top().x;qmin.pop();
if (use[tt]) continue;
use[tt]=1;
for (int i=lin[t][tt];i;i=e[i].next)
if (dis[t][tt]+e[i].v<dis[t][e[i].y]){
dis[t][e[i].y]=dis[t][tt]+e[i].v;
qmin.push(state(e[i].y,dis[t][e[i].y]));
}
}
}
Abigail into(){
scanf("%d%d%d%d",&n,&b,&s,&m);
int x,y;LL l;
for (int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&l);
ins(0,x,y,l);ins(1,y,x,l);
}
}
Abigail work(){
dijkstra(0,b+1);
dijkstra(1,b+1);
for (int i=1;i<=b;i++)
ord[i].x=i,ord[i].v=dis[1][i]+dis[0][i];
sort(ord+1,ord+1+b);
for (int i=1;i<=n;i++)
sum[i]=sum[i-1]+LL(dis[0][ord[i].x])+LL(dis[1][ord[i].x]);
for (int i=1;i<=b;i++)
f[now][i]=sum[i]*LL(i-1);
for (int t=2;t<=s;t++){
now^=1;old^=1;
for (int i=0;i<=b;i++)
f[now][i]=INF;
for (int i=1;i<=b;i++)
for (int j=i-i/t;j<i;j++)
f[now][i]=min(f[now][i],f[old][j]+(sum[i]-sum[j])*LL(i-j-1));
}
}
Abigail outo(){
printf("%lld\n",f[now][b]);
}
int main(){
freopen("assignment.in","r",stdin);
freopen("assignment.out","w",stdout);
into();
work();
outo();
return 0;
}