zoukankan      html  css  js  c++  java
  • 第七次报告和标准的练习过程中解决

      比较大的坡度难度的课题,注册很水的问题,最难的问题是非常困难的;童鞋们也能看到一些问题进行了直接从主要采取OJ的ACM称号,所以,总体来说难度可以说是大多数机器的问题和以前的练习。习的过题数计入平时成绩,能够看作一次超长时长的上机,终于board的情况能够说在意料之中,但也稍微超出了一点预估;希望成绩中没有非常大的水分。

      A. 邀请函

      难度中等偏下。

    求有向图中一个点出发到全部点再返回的最短路之和。

    我们注意到从一个点到全部点就是纯粹的单源最短路;而从全部点返回一个点。倘若我们把图中全部的边反向,就又变成了单源最短路。所以这道题的做法就是对原图和反图各求一遍单源最短路。这里请童鞋们自行区分一下反图和补图的概念。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN=1005;
    const int INF=0x3f3f3f3f;
    int g[MAXN][MAXN],dist[MAXN],n;
    bool vis[MAXN];
    void dijkstra(int src)
    {
        memset(dist,0x3f,sizeof(dist));
        memset(vis,false,sizeof(vis));
        dist[src]=0;
        for(int i=1; i<=n; ++i)
        {
            pair<int,int> tmp=make_pair(INF,-1);
            for(int j=1; j<=n; ++j)
                if(!vis[j]&&dist[j]<tmp.first)
                    tmp=make_pair(dist[j],j);
            if(!~tmp.second)
                break;
            vis[tmp.second]=true;
            for(int j=1; j<=n; ++j)
                dist[j]=min(dist[j],tmp.first+g[tmp.second][j]);
        }
    }
    int main()
    {
        int m,u,v,l;
        while(~scanf("%d%d",&n,&m))
        {
            memset(g,0x3f,sizeof(g));
            for(int i=1; i<=n; ++i)
                g[i][i]=0;
            while(m--)
            {
                scanf("%d%d%d",&u,&v,&l);
                g[u][v]=min(g[u][v],l);
            }
            int ans=0;
            dijkstra(1);
            for(int i=1; i<=n; ++i)
                ans+=dist[i];
            for(int i=1; i<=n; ++i)
                for(int j=i+1; j<=n; ++j)
                    swap(g[i][j],g[j][i]);
            dijkstra(1);
            for(int i=1; i<=n; ++i)
                ans+=dist[i];
            printf("%d
    ",ans);
        }
    }
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int MAXN=1005;
    const int MAXM=100005;
    struct graph
    {
        int head[MAXN];
        int to[MAXM];
        int next[MAXM];
        int len[MAXM];
        int tot;
        void init()
        {
            tot=0;
            memset(head,0xff,sizeof(head));
        }
        void add(int u,int v,int w)
        {
            to[tot]=v;
            len[tot]=w;
            next[tot]=head[u];
            head[u]=tot++;
        }
    } g,rg;
    int dist[MAXN];
    bool inque[MAXN];
    void spfa(graph &G,int src)
    {
        memset(dist,0x3f,sizeof(dist));
        memset(inque,false,sizeof(inque));
        dist[src]=0;
        queue<int> q;
        q.push(src);
        inque[src]=true;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            inque[x]=false;
            for(int i=G.head[x]; ~i; i=G.next[i])
            {
                int y=G.to[i];
                if(dist[x]+G.len[i]<dist[y])
                {
                    dist[y]=dist[x]+G.len[i];
                    if(!inque[y])
                    {
                        q.push(y);
                        inque[y]=true;
                    }
                }
            }
        }
    }
    int main()
    {
        int n,m,u,v,l;
        while(~scanf("%d%d",&n,&m))
        {
            g.init();
            rg.init();
            while(m--)
            {
                scanf("%d%d%d",&u,&v,&l);
                g.add(u,v,l);
                rg.add(v,u,l);
            }
            int ans=0;
            spfa(g,1);
            for(int i=1; i<=n; ++i)
                ans+=dist[i];
            spfa(rg,1);
            for(int i=1; i<=n; ++i)
                ans+=dist[i];
            printf("%d
    ",ans);
        }
    }

      B. 寻找下界

      这道题我预谋已久了……题目原打算直接起名叫lower_bound,后来不希望大家使用STL,故而改成不伦不类的中文名;最后干脆直接禁掉了可能利用lower_bound()函数的全部头文件。原因非常easy。二分尽管是每个程序员必须掌握的技巧,但又快又准地写好二分相同是一个学问,找一个序列中的某个数仅仅只是是最基础的操作罢了。

    这道题,找的就是大于等于某个数的第一个数,同理,希望大家能自行练习寻找一个序列中大于某个数的第一个数(即upper_bound()函数)。话不多说,扔上三种姿势。

    #include<cstdio>
    using namespace std;
    const int MAXN=100005;
    int a[MAXN],n;
    int lower_bound(int x)
    {
        int l=0,r=n;
        while(l<r)
        {
            int m=l+r>>1;
            x>a[m]?

    l=m+1:r=m; } return l; } int main() { int m,k; while(~scanf("%d%d",&n,&m)) { for(int i=0; i<n; ++i) scanf("%d",&a[i]); while(m--) { scanf("%d",&k); int idx=lower_bound(k); printf("%d ",idx==n?-1:a[idx]); } } }

    #include<cstdio>
    using namespace std;
    const int MAXN=100005;
    int a[MAXN],n;
    int lower_bound(int x)
    {
        int l=0,r=n-1;
        while(l<=r)
        {
            int m=l+r>>1;
            x>a[m]?l=m+1:r=m-1;
        }
        return l;
    }
    int main()
    {
        int m,k;
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=0; i<n; ++i)
                scanf("%d",&a[i]);
            while(m--)
            {
                scanf("%d",&k);
                int idx=lower_bound(k);
                printf("%d
    ",idx==n?-1:a[idx]);
            }
        }
    }
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN=100005;
    int a[MAXN];
    int main()
    {
        int n,m,k;
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=0; i<n; ++i)
                scanf("%d",&a[i]);
            while(m--)
            {
                scanf("%d",&k);
                int idx=lower_bound(a,a+n,k)-a;
                printf("%d
    ",idx==n?-1:a[idx]);
            }
        }
    }

      C. Cache

      由于期末上机考试的临近,我们出了这道题,算是对链表操作的回想。这道题其意义在于考查了链表链节的合并和分离操作,倘若能够熟练处理这两个操作,事实上就具备了写块状链表的一切知识和技巧。块状链表不在课程要求内。但有心搞ACM的童鞋最好还是深入探究一下,自行查找资料并手动实现出块状链表的结构和功能。

      由于有些童鞋说这道题用STL写各种RE,于是我就写了一个能够AC的STL版本号供參考。

    #include<iostream>
    #include<string>
    #include<list>
    using namespace std;
    list<string> data;
    list<string>::iterator cur,tmp;
    int main()
    {
        int n,m,k;
        while(cin>>n>>m)
        {
            string str,op;
            while(n--)
            {
                cin>>str;
                data.push_back(str);
            }
            cur=data.begin();
            while(m--)
            {
                cin>>op;
                switch(op[4])
                {
                case 'R':
                    ++cur;
                    if(cur==data.end())
                        --cur;
                    break;
                case 'L':
                    if(cur!=data.begin())
                        --cur;
                    break;
                case 'I':
                    tmp=cur;
                    ++tmp;
                    if(tmp!=data.end())
                    {
                        (*cur)+=(*tmp);
                        data.erase(tmp);
                    }
                    break;
                case 'D':
                    cin>>k;
                    str=(*cur).substr(k,(*cur).size()-k);
                    (*cur)=(*cur).substr(0,k);
                    tmp=cur;
                    ++tmp;
                    data.insert(tmp,str);
                    break;
                }
            }
            for(tmp=data.begin(); tmp!=data.end(); ++tmp)
                cout<<(*tmp)<<' ';
            cout<<'
    '<<(*cur)<<'
    ';
            data.clear();
        }
    }

      D. 行者无疆

      从过题数能够看出。这是毫无疑问的最难题。大概一个多月前的一次训练赛,我和Thor合力搞了将近一个小时才做出来。只是当时那道题时限更严苛。后来我们总结了最短路的一些时间复杂度上的经验并写在了这篇blog里。我的做法是如果起点和终点也有充电桩,然后以每一个充电桩为起点跑一遍单源最短路,接着把全部的充电桩放在一起又一次建图,倘若之间的最短路径小于要求则有一条边。然后再从起点跑一遍单源最短路就可以。当然。这道题也能够直接改动dijkstra,理论上速度会更快,但显然从思考难度上又高了一些,这里不再讨论。

      由于有两次建图且分别求了单源最短路。第一次是稀疏图,第二次是稠密图,所以我多写了一种用spfa处理第一次建图的写法,由于稀疏图下spfa相对于dijkstra的优势还是比較明显的,而稠密图下又会慢于dijkstra。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN=305;
    const int INF=0x3f3f3f3f;
    int city[MAXN][MAXN],pile[MAXN][MAXN],s[MAXN],dist[MAXN];
    bool vis[MAXN];
    void dijkstra(int g[][MAXN],int n,int src)
    {
        memset(dist,0x3f,sizeof(dist));
        memset(vis,false,sizeof(vis));
        dist[src]=0;
        for(int i=1; i<=n; ++i)
        {
            pair<int,int> tmp=make_pair(INF,-1);
            for(int j=1; j<=n; ++j)
                if(!vis[j]&&dist[j]<tmp.first)
                    tmp=make_pair(dist[j],j);
            if(!~tmp.second)
                break;
            vis[tmp.second]=true;
            for(int j=1; j<=n; ++j)
                dist[j]=min(dist[j],tmp.first+g[tmp.second][j]);
        }
    }
    int main()
    {
        int n,m,v,k,x,y,d;
        while(~scanf("%d%d%d",&n,&m,&v))
        {
            memset(city,0x3f,sizeof(city));
            for(int i=1; i<=n; ++i)
                city[i][i]=0;
            memset(pile,0x3f,sizeof(pile));
            while(m--)
            {
                scanf("%d%d%d",&x,&y,&d);
                if(d<city[x][y])
                    city[x][y]=city[y][x]=d;
            }
            scanf("%d",&k);
            for(int i=1; i<=k; ++i)
                scanf("%d",&s[i]);
            s[++k]=1;
            s[++k]=n;
            for(int i=1; i<=k; ++i)
                pile[i][i]=0;
            for(int i=1; i<=k; ++i)
            {
                dijkstra(city,n,s[i]);
                for(int j=i+1; j<=k; ++j)
                {
                    int d=dist[s[j]];
                    if(d>v*5)
                        d=INF;
                    pile[i][j]=pile[j][i]=d;
                }
            }
            dijkstra(pile,k,k-1);
            printf("%d
    ",dist[k]==INF?

    -1:dist[k]); } }

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int MAXN=305;
    const int MAXM=20005;
    const int MAXK=55;
    const int INF=0x3f3f3f3f;
    struct graph
    {
        int head[MAXN];
        int to[MAXM];
        int next[MAXM];
        int len[MAXM];
        int tot;
        void init()
        {
            tot=0;
            memset(head,0xff,sizeof(head));
        }
        void add(int u,int v,int w)
        {
            to[tot]=v;
            len[tot]=w;
            next[tot]=head[u];
            head[u]=tot++;
        }
    } city;
    int dist[MAXN];
    bool inque[MAXN];
    void spfa(int src)
    {
        memset(dist,0x3f,sizeof(dist));
        memset(inque,false,sizeof(inque));
        dist[src]=0;
        queue<int> q;
        q.push(src);
        inque[src]=true;
        while(!q.empty())
        {
            int x=q.front();
            q.pop();
            inque[x]=false;
            for(int i=city.head[x]; ~i; i=city.next[i])
            {
                int y=city.to[i];
                if(dist[x]+city.len[i]<dist[y])
                {
                    dist[y]=dist[x]+city.len[i];
                    if(!inque[y])
                    {
                        q.push(y);
                        inque[y]=true;
                    }
                }
            }
        }
    }
    int pile[MAXK][MAXK],s[MAXK],k;
    bool vis[MAXK];
    void dijkstra(int src)
    {
        memset(dist,0x3f,sizeof(dist));
        memset(vis,false,sizeof(vis));
        dist[src]=0;
        for(int i=1; i<=k; ++i)
        {
            pair<int,int> tmp=make_pair(INF,-1);
            for(int j=1; j<=k; ++j)
                if(!vis[j]&&dist[j]<tmp.first)
                    tmp=make_pair(dist[j],j);
            if(!~tmp.second)
                break;
            vis[tmp.second]=true;
            for(int j=1; j<=k; ++j)
                dist[j]=min(dist[j],tmp.first+pile[tmp.second][j]);
        }
    }
    int main()
    {
        int n,m,v,x,y,d;
        while(~scanf("%d%d%d",&n,&m,&v))
        {
            city.init();
            while(m--)
            {
                scanf("%d%d%d",&x,&y,&d);
                city.add(x,y,d);
                city.add(y,x,d);
            }
            scanf("%d",&k);
            for(int i=1; i<=k; ++i)
                scanf("%d",&s[i]);
            s[++k]=1;
            s[++k]=n;
            memset(pile,0x3f,sizeof(pile));
            for(int i=1; i<=k; ++i)
                pile[i][i]=0;
            for(int i=1; i<=k; ++i)
            {
                spfa(s[i]);
                for(int j=i+1; j<=k; ++j)
                {
                    int d=dist[s[j]];
                    if(d>v*5)
                        d=INF;
                    pile[i][j]=pile[j][i]=d;
                }
            }
            dijkstra(k-1);
            printf("%d
    ",dist[k]==INF?-1:dist[k]);
        }
    }

      E. 多层礼包

      水题,对栈的复习,不用栈也能做,不再多说。

    #include<cstdio>
    #include<stack>
    using namespace std;
    char str[105];
    int main()
    {
        while(~scanf("%s",&str))
        {
            stack<char> s;
            for(int i=0; str[i]!='G'; ++i)
                switch(str[i])
                {
                case '[':
                    s.push(str[i]);
                    break;
                case ']':
                    if(!s.empty())
                        s.pop();
                    break;
                }
            printf("%d
    ",s.size());
        }
    }
    #include<cstdio>
    using namespace std;
    char str[105];
    int main()
    {
        while(~scanf("%s",&str))
        {
            int ans=0;
            for(int i=0; str[i]!='G'; ++i)
                switch(str[i])
                {
                case '[':
                    ++ans;
                    break;
                case ']':
                    if(ans>0)
                        --ans;
                    break;
                }
            printf("%d
    ",ans);
        }
    }

      F. 无线网络

      这道题的难度在于思路。

    题目给出了全部点的坐标。然后非常多人就不知道这是一道什么类型的题了;实质上是给出了一个全然图。然后我们注意到。实现随意寝室的互联,并不须要直接互联。换言之,随意寝室的互联只是寻找一个最小生成树。

    然后是怎样处理电力猫的问题,我们注意到电力猫相当于超级版的路由器。实现路由器的功能又不考虑距离,所以如果有k个电力猫,实质上我们是在找最小生成树的第k大边的长度。更具体的讨论能够看POJ原题的Discuss。更具体的证明须要讨论最小生成树和连通分量的关系,能够看这篇文章的第11~12页。此外,这道题是一个全然图。换言之是个非常稠密的图。用prim是比kruskal好得多的选择,尽管我控制了数据量让它们都能过。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int MAXN=505;
    const int INF=0x3f3f3f3f;
    bool vis[MAXN];
    double g[MAXN][MAXN],lowc[MAXN],len[MAXN];
    int n,k,x[MAXN],y[MAXN];
    inline int sqr(int x)
    {
        return x*x;
    }
    double prim()
    {
        memset(vis,false,sizeof(vis));
        for(int i=1; i<=n; ++i)
            lowc[i]=g[1][i];
        vis[1]=true;
        int cnt=0;
        for(int i=1; i<n; ++i)
        {
            int mark=-1;
            double minc=INF;
            for(int j=1; j<=n; ++j)
                if(!vis[j]&&minc>lowc[j])
                {
                    minc=lowc[j];
                    mark=j;
                }
            if(!~mark)
                return -1;
            len[cnt++]=minc;
            vis[mark]=true;
            for(int j=1; j<=n; ++j)
                if(!vis[j]&&lowc[j]>g[mark][j])
                    lowc[j]=g[mark][j];
        }
        sort(len,len+cnt);
        for(int i=0;i<cnt;++i)
            printf("%.2f ",len[i]);
        putchar('
    ');
        return len[cnt-k];
    }
    int main()
    {
        while(~scanf("%d%d",&n,&k))
        {
            for(int i=1; i<=n; ++i)
                scanf("%d%d",&x[i],&y[i]);
            for(int i=1; i<=n; ++i)
                for(int j=i; j<=n; ++j)
                    g[i][j]=g[j][i]=sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j]));
            printf("%.2f
    ",prim());
        }
    }
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int MAXN=505;
    const int MAXM=MAXN*MAXN;
    inline int sqr(int x)
    {
        return x*x;
    }
    struct edge
    {
        int u,v;
        double w;
        edge(int _u=0,int _v=0,double _w=0):u(_u),v(_v),w(_w) {}
        bool operator<(const edge &oth) const
        {
            return w<oth.w;
        }
    } e[MAXM];
    int n,m,k,x[MAXN],y[MAXN],u[MAXN];
    void init()
    {
        for(int i=1; i<=n; ++i)
            u[i]=i;
    }
    int find(int x)
    {
        if(u[x]!=x)
            u[x]=find(u[x]);
        return u[x];
    }
    void merge(int x,int y)
    {
        u[find(x)]=find(y);
    }
    double kruskal()
    {
        if(n-k==0)
            return 0;
        sort(e,e+m);
        int cnt=0;
        init();
        for(int i=0; i<m; ++i)
            if(find(e[i].u)!=find(e[i].v))
            {
                merge(e[i].u,e[i].v);
                if(++cnt==n-k)
                    return e[i].w;
            }
    }
    int main()
    {
        while(~scanf("%d%d",&n,&k))
        {
            for(int i=1; i<=n; ++i)
                scanf("%d%d",&x[i],&y[i]);
            m=0;
            for(int i=1; i<=n; ++i)
                for(int j=i+1; j<=n; ++j)
                {
                    double l=sqrt(sqr(x[i]-x[j])+sqr(y[i]-y[j]));
                    e[m++]=edge(i,j,l);
                    e[m++]=edge(j,i,l);
                }
            printf("%.2f
    ",kruskal());
        }
    }

      G. barty的智商

      首先说句题外话。barty是北航有史以来第一支也是眼下唯一一支ACM Final队的成员,是GG的队友,专攻图论。这道题也是以前的一道校赛原题,然而它并不难。它突出的思想是怎样把一个求解性问题转化成验证性问题。对于这道题。我们寻找满足要求的最低智商是困难的。但对于某一个智商值,我们推断它是否满足要求是easy的,仅仅要跑一遍拓扑排序就能够了。于是能够产生二分答案的思想,就是在智商值的范围内进行二分,对于每个值进行验证;二分过程和B题极其相似,也是在找一个下界(从而也能够看出对于二分,像书上单纯的查找某个值是否存在是最基础的,而B题的二分方式相同是有意义的)。

      验证的时候能够选择建立新图,这里使用了vector模拟邻接表。

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<queue>
    using namespace std;
    const int MAXN=10005;
    const int INF=0x3f3f3f3f;
    vector<pair<int,int> > G[MAXN];
    vector<int> g[MAXN];
    int du[MAXN],n;
    bool toposort()
    {
        memset(du,0,sizeof(du));
        for(int i=1; i<=n; ++i)
            for(int j=0; j<g[i].size(); ++j)
                ++du[g[i][j]];
        int tot=0;
        queue<int> q;
        for(int i=1; i<=n; ++i)
            if(!du[i])
                q.push(i);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            ++tot;
            for(int i=0; i<g[u].size(); ++i)
            {
                int v=g[u][i];
                if(!(--du[v]))
                    q.push(v);
            }
        }
        return tot==n;
    }
    int main()
    {
        int t,m,a,b,c;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1; i<=n; ++i)
                G[i].clear();
            while(m--)
            {
                scanf("%d%d%d",&a,&b,&c);
                G[b].push_back(make_pair(a,c));
            }
            int l=0,r=INF;
            while(l<r)
            {
                int m=l+r>>1;
                for(int i=1; i<=n; ++i)
                {
                    g[i].clear();
                    for(int j=0; j<G[i].size(); ++j)
                        if(G[i][j].second>m)
                            g[i].push_back(G[i][j].first);
                }
                toposort()?r=m:l=m+1;
            }
            printf("%d
    ",l);
        }
    }

      也能够不建新图。对拓扑排序稍作修改。用前向星来写的邻接表。

    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int MAXN=10005;
    const int MAXM=10005;
    const int INF=0x3f3f3f3f;
    struct graph
    {
        int head[MAXN];
        int to[MAXM];
        int next[MAXM];
        int len[MAXM];
        int tot;
        void init()
        {
            tot=0;
            memset(head,0xff,sizeof(head));
        }
        void add(int u,int v,int w=-1)
        {
            to[tot]=v;
            len[tot]=w;
            next[tot]=head[u];
            head[u]=tot++;
        }
    } g;
    int du[MAXN],n;
    bool toposort(int k)
    {
        memset(du,0,sizeof(du));
        for(int i=1; i<=n; ++i)
            for(int j=g.head[i]; ~j; j=g.next[j])
                if(g.len[j]>k)
                    ++du[g.to[j]];
        int tot=0;
        queue<int> q;
        for(int i=1; i<=n; ++i)
            if(!du[i])
                q.push(i);
        while(!q.empty())
        {
            int u=q.front();
            q.pop();
            ++tot;
            for(int i=g.head[u]; ~i; i=g.next[i])
                if(g.len[i]>k)
                {
                    int v=g.to[i];
                    if(!(--du[v]))
                        q.push(v);
                }
        }
        return tot==n;
    }
    int main()
    {
        int t,m,a,b,c;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            g.init();
            while(m--)
            {
                scanf("%d%d%d",&a,&b,&c);
                g.add(a,b,c);
            }
            int l=0,r=INF;
            while(l<r)
            {
                int m=l+r>>1;
                toposort(m)?r=m:l=m+1;
            }
            printf("%d
    ",l);
        }
    }

      总的来说,我想给全部坚持独立完毕题目的童鞋点个赞。这次的题目可做性非常强,但也确实非常有难度,坚持做下来的人非常值得鼓舞。期末考试临近,希望大家好好复习重点的数据结构和算法,期末上机和笔试加油~(尽管上机是我们出题,写我们的外表似参加平地机or合分……)

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    呃,如何使 .NET 程序,在 64位 系统 中,以 32位 模式运行。
    [转载]Cortana 设计指导方针
    Could not load file or assembly System.Core, Version=2.0.5.0
    wpf中用户控件的属性重用
    浅谈AutoResetEvent的用法
    WPF异步载入图片,附带载入中动画
    WPFLoading遮层罩
    获取WPF的DataGrid控件中,是否存在没有通过错误验证的Cell
    WPF通过异常来验证用户输入
    WPF验证之——必填验证
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/4708879.html
Copyright © 2011-2022 走看看