1. 程式人生 > >day2018.10.21模擬賽總結

day2018.10.21模擬賽總結

今天是NOIP考前模擬賽day3了.

發揮得海星,自我感覺良好.

T1:

題目大意:給定三種氣球的數量a,b,c,兩種或三種氣球可以3個組成一組,求最多能分多少組.

考場得分:100.

顯然我們設兩種氣球的數量為x,y,當x<y2x\geq y時,這兩種氣球的最多組數就是\frac{x+y}{3}.

那麼我們將讀入進來的三個數排序之後存入a,b,c.當a為0時,我們發現答案就是\frac{b+c}{3};不為0時分兩種情況,一種是2(a+b)\leq c,那麼答案就為a+b;另一種是2(a+b)> c,那麼答案就為\frac{a+b+c}{3}.

程式碼如下:

#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個任務的最小距離和,那麼就可以列出方程:

f[i][j]=min_{k\in [0,i-1]}(f[k][j-1]+sum[k+1][i])

其中sum[i][j]表示排序後在區間[i,j]裡的點分配在一個任務裡的貢獻,可以預處理出.

我們發現這個演算法十分優秀,時間複雜度為O(n^3),能夠拿到65分的高分,但是我因為開了三個5000^2的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一定滿足k\in [i-i/j,i).

所以時間複雜度為O(n^2*\sum_{i=1}^{n}\frac{1}{i}),由於調和級數約等於log,所以時間複雜度為O(n^2logn),由於實現較為寬鬆且調和級數跑不滿,所以可以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;
}