zoukankan      html  css  js  c++  java
  • APIO2017伪题解

    题目质量还是比较高的,只是当时澳大利亚方面出了一点问题?最后造成了区分度非常迷的局面。

    纵观三道题,T1是披着交互外衣的提答题,T2是披着交互外衣的传统题,T3是一道据说近年来APIO最水的一道传统题,然而对于不敢相信T3水的选手来说只能靠硬搞两道交互拿分了。

    T1 Rainbow

    没脸写题解和程序了。

    Subtask 1直接floodfill一下,Subtask 2听说前缀和讨论一下,后面听说数据结构维护一下。

    T2 Koala

    这个感觉可以写一篇交互库玩耍记了,感觉前几个部分分就是在考调戏交互库的水平。

    首先看Subtask 1只有4分,而且场上超过300人拿了这4分,但是我不会怎么办?

    猜一个结论,随便选一个旁边放一个宝石,其它都不放,然后koala没放宝石的那个就是最小的那个。就过了。(我竟然没想到)

    然后如果光搞Subtask 2感觉想得出结论会比较吃力,因为这题实际上是一道构造题。这个时候就需要在交互库上做文章了。

    看一会代码发现交互库实际上就是一个无脑DP(于是就可以确定交互库占用的时空复杂度了),并没有什么可以利用的地方。但是我们可以把它改成一个能够做实验的程序,可供我们猜结论使用,如下

     1 #include<cstdio>
     2 #include<ctime>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     5 using namespace std;
     6 
     7 int N,W,P[205],b[205],r[205],c[205];
     8 
     9 void playRound(int *B, int *R) {
    10     int i, j;
    11 
    12     int cache[2][205];
    13     int num[2][205];
    14     char taken[105][205];
    15 
    16     for (i=0;i<205;++i) {
    17         cache[1][i] = 0;
    18         num[1][i] = 0;
    19     }
    20 
    21     for (i=0;i<N;++i) {
    22         int v = B[i]+1;
    23         int ii = i&1;
    24         int o = ii^1;
    25         for (j=0;j<=W;++j) {
    26             cache[ii][j] = cache[o][j];
    27             num[ii][j] = num[o][j];
    28             taken[i][j] = 0;
    29         }
    30         for (j=W;j>=v;--j) {
    31             int h = cache[o][j-v] + P[i];
    32             int hn = num[o][j-v] + 1;
    33             if (h > cache[ii][j] || (h == cache[ii][j] && hn > num[ii][j])) {
    34                 cache[ii][j] = h;
    35                 num[ii][j] = hn;
    36                 taken[i][j] = 1;
    37             } else {
    38                 taken[i][j] = 0;
    39             }
    40         }
    41     }
    42 
    43     int cur = W;
    44     for (i=N-1;i>=0;--i) {
    45         R[i] = taken[i][cur] ? (B[i] + 1) : 0;
    46         cur -= R[i];
    47     }
    48 }
    49 
    50 int main(){
    51     srand(time(NULL));
    52     N=100; W=100;
    53     for (int i=0; i<N; i++){
    54         int k=rand()%N+1; while (c[k]) k=rand()%N+1;
    55         c[k]=1; P[i]=k;
    56     }
    57     
    58     b[0]=b[1]=5;
    59     for (int i=2; i<100; i++) b[i]=0;
    60     
    61     playRound(b,r);
    62     for (int i=0; i<N; i++){
    63         printf("%3d%3d ",i,r[i]);
    64         if ((i+1)%10==0) puts("");
    65     }
    66     return 0;
    67 }

    对于如何分配b数组,只需要改主函数里的两句话即可。

    通过实验发现,现在已经确定的区间为[1,100],如果每个位置放一个的话,就可以确定[51,100],然后这部分放2个其余都不放的话可以确定[76,100],这部分再放4个其余不放的话可以确定[92,100],这部分放11个可以确定[100,100]。

    这样我们真好用了4次确定了100的位置。

    考虑如何测试,写一个数据生成程序:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<sys/timeb.h>
     5 #include<cstdlib>
     6 #include<fstream>
     7 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     8 typedef long long ll;
     9 using namespace std;
    10 
    11 ofstream fout,fres;
    12 
    13 const int N=100100;
    14 int b[N];
    15 
    16 void insd(){
    17     struct timeb tp; ftime(&tp);
    18     srand(tp.time*1000+tp.millitm);
    19 }
    20 
    21 int sj(int l,int r){ return rand()%(r-l+1)+l; }
    22 
    23 int main(){
    24     insd();
    25     fout.open("koala.in"); fres.open("result");
    26     ios::sync_with_stdio(false);
    27     
    28     int F=sj(1,3),G=sj(1,100);
    29     fout<<F<<' '<<G<<endl;
    30     if (F==1){
    31         while (G--){
    32             fout<<"100 100 ";
    33             rep(i,0,100) b[i]=0;
    34             rep(i,0,99){
    35                 int k=sj(1,100); while (b[k]) k=sj(1,100);
    36                 fout<<k<<' '; b[k]=1;
    37                 if (k==1) fres<<i<<endl;
    38             }
    39             fout<<endl;
    40         }
    41     }else if (F==2){
    42         while (G--){
    43             fout<<"100 100 ";
    44             rep(i,0,100) b[i]=0;
    45             rep(i,0,99){
    46                 int k=sj(1,100); while (b[k]) k=sj(1,100);
    47                 fout<<k<<' '; b[k]=1;
    48                 if (k==100) fres<<i<<endl;
    49             }
    50             fout<<endl;
    51         }
    52     }else if (F==3){
    53         while (G--){
    54             int x=0,y=0;
    55             fout<<"100 100 ";
    56             rep(i,0,100) b[i]=0;
    57             rep(i,0,99){
    58                 int k=sj(1,100); while (b[k]) k=sj(1,100);
    59                 fout<<k<<' '; b[k]=1;
    60                 if (!x) x=k; else if (!y) fres<<((k>x)?1:0)<<endl,y=k;
    61             }
    62             fout<<endl;
    63         }
    64     }
    65     fout.close(); fres.close();
    66     return 0;
    67 }

    这份代码在生成数据的同时将答案输出到了result文件里,方便对拍。对拍的代码:

    1 g++ make.cpp -o make -g -Wall
    2 g++ koala.cpp grader.cpp -o koala -static -std=c++11
    3 while true; do
    4 echo ---------------------
    5 ./make
    6 time ./koala<koala.in>koala.out
    7 if !(diff -w koala.out result); then exit; fi
    8 done

    现在看Subtask 3,用上面的程序实验发现,其余都不放,如果b[0]和b[1]都放15的话,两个都不会被选。这样我们在[1,15]内二分,每次保证b[0]=b[1],直到发现有一个选了而另一个没选就可以返回答案了。

    这是C<=4的算法,能拿14分,满分是将区间人工细化以减小一次机器搜索。

    前33分程序:

     1 #include "koala.h"
     2 #include<cstdio>
     3 using namespace std;
     4 
     5 const int N=205;
     6 int b[N],r[N],c[N];
     7 
     8 int minValue(int N, int W) {
     9     for (int i=1; i<N; i++) b[i]=0; b[0]=1;
    10     playRound(b,r);
    11     for (int i=0; i<N; i++) if (!r[i]) return i;
    12     return 0;
    13 }
    14 
    15 int maxValue(int N, int W) {
    16     for (int i=0; i<N; i++) b[i]=1,c[i]=0;
    17     playRound(b,r);
    18     for (int i=0; i<N; i++) if (!r[i]) c[i]=1;
    19     for (int i=0; i<N; i++) if (!c[i]) b[i]=2; else b[i]=0;
    20     playRound(b,r);
    21     for (int i=0; i<N; i++) if (r[i]!=3) c[i]=1;
    22     for (int i=0; i<N; i++) if (!c[i]) b[i]=4; else b[i]=0;
    23     playRound(b,r);
    24     for (int i=0; i<N; i++) if (r[i]!=5) c[i]=1;
    25     for (int i=0; i<N; i++) if (!c[i]) b[i]=11; else b[i]=0;
    26     playRound(b,r);
    27     for (int i=0; i<N; i++) if (r[i]==12) return i;
    28     return 0;
    29 }
    30 
    31 int greaterValue(int N, int W) {
    32     int L=0,R=15;
    33     while (L<R){
    34         int mid=(L+R)>>1;
    35         b[0]=b[1]=mid;
    36         for (int i=2; i<N; i++) b[i]=0;
    37         
    38         playRound(b,r);
    39         
    40         if (!r[0] && r[1]) return 1;
    41         if (!r[1] && r[0]) return 0;
    42         
    43         if (!r[0] && !r[1]) R=mid-1; else L=mid+1;
    44     }
    45     return L;
    46 }
    47 
    48 void allValues(int N, int W, int *P) { if (W == 2*N) { } else { } }

    Subtask 4是给数组排序,发现如果b[x]和b[y]各放100个的话,肯定有一个被选而另一个不选,也就是说可以用一次游戏完成两个数的比较。

    所以我们手工实现一个$O(nlog n)$的比较排序算法即可。

    或许可以直接写进std::sort的cmp里,没试过。

    Subtask 5没做。

    T3 merchant

    首先第一眼分数规划,第二眼spfa判负环,问题解决了一半。

    然后发现因为每次买卖涉及两个城市,所以一般做法没办法给每条边赋值。

    考虑根据决策重建图,如果两个点u和v又一次买卖,一定是u的买入价和v的卖出价中差值最大的那个,所以根据这个连边,构成一个边数$n^2$的图,就可以直接跑spfa了。

    比较坑的情况是一个城市同一个商品的卖出价可能大于买入价,也就是说留在这里不走可以一直赚钱。但题目显然是不允许这样的。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define rep(i,l,r) for (int i=l; i<=r; i++)
     4 typedef long long ll;
     5 using namespace std;
     6 
     7 const int N=110,M=1010,inf=1000000000;
     8 int n,m,K,u,v,w,mx,flag,mid,vis[N],val[N][N],mp[N][N],a[N][M],b[N][M];
     9 ll d[N];
    10 
    11 void spfa(int x){
    12     if (flag) return; vis[x]=1;
    13     rep(i,1,n) if (i!=x && mp[x][i]<inf && d[i]>d[x]+1ll*mp[x][i]*mid-val[x][i]){
    14         d[i]=d[x]+1ll*mp[x][i]*mid-val[x][i];
    15         if (vis[i]) { flag=1; return; } else spfa(i);
    16     }
    17     vis[x]=0;
    18 }
    19 
    20 int jud(int mid){
    21     rep(i,1,n) vis[i]=d[i]=0; flag=0;
    22     rep(i,1,n){ spfa(i); if (flag) return 1; }
    23     return 0;
    24 }
    25 
    26 int main(){
    27     freopen("merchant.in","r",stdin);
    28     freopen("merchant.out","w",stdout);
    29     scanf("%d%d%d",&n,&m,&K);
    30     rep(i,1,n) rep(j,1,K) scanf("%d%d",&a[i][j],&b[i][j]),mx=max(mx,b[i][j]);
    31     rep(i,1,n) { rep(j,1,n) mp[i][j]=inf; mp[i][i]=0; }
    32     rep(i,1,m) scanf("%d%d%d",&u,&v,&w),mp[u][v]=min(mp[u][v],w);
    33     rep(k,1,n) rep(i,1,n) rep(j,1,n) mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
    34     rep(i,1,n) rep(j,1,n) rep(k,1,K)
    35         if (i!=j && ~a[i][k] && ~b[j][k]) val[i][j]=max(val[i][j],b[j][k]-a[i][k]);
    36     int L=0,R=mx,ans=0;
    37     while (L<=R){
    38         mid=(L+R)>>1;
    39         if (jud(mid)) ans=mid,L=mid+1; else R=mid-1;
    40     }
    41     printf("%d
    ",ans);
    42     return 0;
    43 }
  • 相关阅读:
    HBase- 安装单机版HBase
    javascript中的设计模式之模板方法模式
    win 设置自动启动软件
    php高精度加减乘除
    frp实现内网穿透,实现夸服务器访问
    OCM 12c 直考预备知识点
    Oracle 19c New Features : Active Data Guard DML Redirect
    3级搭建类302-Oracle 19c RAC 双节点搭建
    VMWare WorkStation 15.5 配置RAC共享存储节点二无法识别共享磁盘UUID解决办法
    你还在争论 count(*) 与 count(column) 哪个更快?
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8903603.html
Copyright © 2011-2022 走看看