zoukankan      html  css  js  c++  java
  • APIO 2013

    这套好丧……跟别的画风好不一样(中国风?)。提答没做也没测,假装只有两题吧。140/200

    T1.ROBOTS

    题目大意:h*w的网格上有n个机器人编号1~n,网格上有空地、墙、顺/逆时针转向器,每次可以把一个机器人朝一个方向推,机器人碰到空地会继续前进,碰到转向器会转向,碰到墙会在前一格停止,同一时间只能有一个机器人在动,编号连续的机器人可以合体,例如2号和3号合成[2,3],[2,3]和[4,6]合成[2,6],问把所有机器人合成一个最少要推几下,无解输出-1。(n<=9,h,w<=500)

    思路:先记忆化搜索预处理出每个格子朝四个方向推各会推到哪里(注意可能会无限循环),用f[i][j][k][l]表示[i,j]并成一个机器人位于(k,l)格至少要推几下(内存较紧,需要hash),考虑dijkstra,堆的log难以接受,我想了个方法:答案肯定不会太大,于是每种权值开一个队列,这样复杂度是每次转移O(1),由于还要区间合并,最后时间复杂度大概是O(n^3hw),空间同样。不过实际上这个n^3不是满的,所以理论上应该是很科学的,不过我写了vector,常数好像有点大,本来想手写个链表加上内存回收搞不好就过了,因为懒而且还要打T2暴力就放弃了,最后得分85/100(都是T)。

    #include<cstdio>
    #include<cstdlib>
    #include<vector>
    #include<queue>
    using namespace std;
    #define MN 500
    #define MQ 11250000
    struct pos{int x,y;}t[MN+5][MN+5][4];
    const int o[4][2]={{0,-1},{-1,0},{0,1},{1,0}};
    char g[MN+5][MN+5];
    int d[MQ+5],p[9][9],pl[45],pr[45],pn,x,y,l,r;
    pos work(int x,int y,int f)
    {
        if(t[x][y][f].x)return t[x][y][f];
        t[x][y][f]=(pos){-1,-1};
        int r;
        if(g[x][y]=='.'||(g[x][y]>='1'&&g[x][y]<='9'))r=f;
        if(g[x][y]=='A')r=f?f-1:3;
        if(g[x][y]=='C')r=f<3?f+1:0;
        return t[x][y][f]=(g[x][y]&&g[x][y]!='x')?
            work(x+o[r][0],y+o[r][1],r):(pos){x-o[f][0],y-o[f][1]};
    }
    inline int hash(int x,int y,int l,int r){return x<0?-1:((x-1)*500+y-1)*pn+p[l][r];}
    inline void dehash(int z){l=pl[z%pn];r=pr[z%pn];y=(z/=pn)%500+1;x=z/500+1;}
    struct MyPQ
    {
        vector<queue<int> > v;int x;
        void push(int x,int z)
        {
            if(x<0||(d[x]&&d[x]<=z))return;d[x]=z;
            while(v.size()<z)v.push_back(queue<int>());
            v[z-1].push(x);
        }
        inline int top(){return v[x].front();}
        inline void next(){for(v[x].pop();v[x].empty()&&x<v.size();++x);if(x==v.size())puts("-1"),exit(0);}
        inline void pop(){for(next();x>=d[v[x].front()];next());}
    }q;
    int main()
    {
        int n,w,h,i,j,k,z,zz;
        scanf("%d%d%d",&n,&w,&h);--n;
        for(i=1;i<=h;++i)scanf("%s",g[i]+1);
        for(i=0;i<9;++i)for(j=i;j<9;++j)pl[pn]=i,pr[pn]=j,p[i][j]=pn++;
        for(i=1;i<=h;++i)for(j=1;j<=w;++j)
        {
            for(k=0;k<4;++k)work(i,j,k);
            if(g[i][j]>='1'&&g[i][j]<='9')q.push(hash(i,j,g[i][j]-'1',g[i][j]-'1'),1);
        }
        while(true)
        {
            dehash(z=q.top());q.pop();
            if(l==0&&r==n)return printf("%d",d[z]-1),0;
            for(i=0;i<l;++i)if(d[zz=hash(x,y,i,l-1)])q.push(hash(x,y,i,r),d[z]+d[zz]-1);
            for(i=8;i>r;--i)if(d[zz=hash(x,y,r+1,i)])q.push(hash(x,y,l,i),d[z]+d[zz]-1);
            for(j=0;j<4;++j)q.push(hash(t[x][y][j].x,t[x][y][j].y,l,r),d[z]+1);
        }
    }
    View Code

    正解:双队列广搜,每种机器人先出算出可以合成它的所有小机器人在各个位置的情况,然后开两个队列,一个是按从小机器人合成的按权值排序的队列,一个是被推着走拓展出的队列,每次取最小的,这种做法理论上和我的是同阶的,不过常数上感觉比我优到不知道哪里去了。

    T2.TOLL

    题目大意:给出一张N点M边的带权联通无向图,每个点上有一些人,给定K条边,要求你定边权后加入原图,使得最后最小生成树上,每个人走到1号点,路径上经过的新加的边的权值和最大。(N<=100,000,M<=300,000,K<=20)

    思路:暴力枚举最后保留哪些新加的边,对原图做最小生成树后,每次加入一条非树边,O(n)算出每条边被保留下最大能取的权值,并算出答案,复杂度O(MlogM+NK*2^K),得分55/100。

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    inline int read()
    {
        int x=0;char c;
        while((c=getchar())<'0'||c>'9');
        for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 100000
    #define MM 300000
    #define MK 20
    struct Edge{int x,y,w;}E[MM+5],S[MN+5],K[MK+5],T[MN+5];
    bool cmp(Edge a,Edge b){return a.w<b.w;}
    struct edge{int nx,t,w,u,f;}e[MN*2+50];
    int n,k,f[MN+5],h[MN+5],en,u[MN+5],p[MN+5],mx[MK+5];
    int fa[MN+5],tf[MN+5],d[MN+5],ps[MN+5];
    ll ans,fs[MN+5];
    int gf(int k){return f[k]?f[k]=gf(f[k]):k;}
    inline void ins(int x,int y,int w,int f=0)
    {
        e[++en]=(edge){h[x],y,w,0,f};h[x]=en;
        e[++en]=(edge){h[y],x,w,0,f};h[y]=en;
    }
    void pre(int x)
    {
        for(int i=h[x];i;i=e[i].nx)if(!e[i].u&&e[i].t!=fa[x])
            d[e[i].t]=d[x]+1,fa[e[i].t]=x,tf[e[i].t]=i,pre(e[i].t);
    }
    void dp(int x,int fa)
    {
        ps[x]=p[x];fs[x]=0;
        for(int i=h[x];i;i=e[i].nx)if(!e[i].u&&e[i].t!=fa)
        {
            dp(e[i].t,x);
            ps[x]+=ps[e[i].t];
            fs[x]+=fs[e[i].t];
            if(e[i].f)fs[x]+=(ll)mx[e[i].f]*ps[e[i].t];
        }
    }
    void work()
    {
        int i,j=0,x,y,z,c;
        memset(f,0,sizeof(int)*(n+1));
        for(i=1;i<=k;++i)if(u[i]&&gf(K[i].x)!=gf(K[i].y))
            f[gf(K[i].x)]=gf(K[i].y),T[++j]=K[i];
        memset(h,0,sizeof(int)*(n+1));en=1;
        for(i=1;i<n;++i)ins(S[i].x,S[i].y,S[i].w);
        memset(mx,127,sizeof(mx));
        for(i=1;i<=j;++i)
        {
            pre(1);
            for(x=T[i].x,y=T[i].y,z=-1;x!=y;)
                if(d[x]>d[y]){if(e[tf[x]].w&&e[tf[x]].w>z)z=e[tf[x]].w,c=tf[x];x=fa[x];}
                         else{if(e[tf[y]].w&&e[tf[y]].w>z)z=e[tf[y]].w,c=tf[y];y=fa[y];}
            e[c].u=e[c^1].u=1;mx[i]=z;
            for(x=T[i].x,y=T[i].y;x!=y;)
                if(d[x]>d[y]){if(!e[tf[x]].w)mx[e[tf[x]].f]=min(mx[e[tf[x]].f],z);x=fa[x];}
                         else{if(!e[tf[y]].w)mx[e[tf[y]].f]=min(mx[e[tf[y]].f],z);y=fa[y];}
            ins(T[i].x,T[i].y,0,i);
        }
        dp(1,0);
        if(fs[1]>ans)ans=fs[1];
    }
    void dfs(int x)
    {
        if(x>k){work();return;}
        u[x]=0;dfs(x+1);
        u[x]=1;dfs(x+1);
    }
    int main()
    {
        freopen("toll.in","r",stdin);
        freopen("toll.out","w",stdout);
        int m,i,j;
        n=read();m=read();k=read();
        for(i=0;i<m;++i)E[i].x=read(),E[i].y=read(),E[i].w=read();
        sort(E,E+m,cmp);
        for(i=j=0;i<m;++i)if(gf(E[i].x)!=gf(E[i].y))
            f[gf(E[i].x)]=gf(E[i].y),S[++j]=E[i];
        for(i=1;i<=k;++i)K[i].x=read(),K[i].y=read();
        for(i=1;i<=n;++i)p[i]=read();
        dfs(1);
        cout<<ans;
        fclose(stdin);fclose(stdout);return 0;
    }
    View Code

    正解:一开始先把K条边边权设为0和M条边放一起跑最小生成树,K条边把最小生成树分成K+1块,每一块最后无论如何一定是连在一起的,可以缩成一个点,然后再按上面的暴力做,复杂度O(MlogM+K^2*2^K)。

  • 相关阅读:
    C#+API实现指定窗体激活
    DEVC++学习之(一)
    javascript 实现原生下载的各种情况
    IssueVision 之WebService安全篇
    Add relationship to BS sample
    ExpandRelationWithCtxt 与 GetRelatedObjects 的区别
    C#调用javascript
    解禁网页限制
    Unix cc options vs gcc options
    IssueVision 之模式篇
  • 原文地址:https://www.cnblogs.com/ditoly/p/APIO-2013.html
Copyright © 2011-2022 走看看