day2018.10.21模拟赛总结

版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/83240586

今天是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;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/83240586
今日推荐