第五次上机赛解题报告及标程

  这次上机的题目考查的内容以图论的一些基础算法为主,我实话实话这次上机题有出失误的地方,个别题的难度没有控制好,所以过题情况不佳也是情理之中。

  A. 对称二叉树

  与上一次上机的同构二叉树很相似,这不过这道题要求的是轴对称;事实上也只要在那道题的基础上改动几处代码就可以了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int MAXN=1005;
struct BTNode
{
    char data[10];
    BTNode *lchild,*rchild;
};
char str[MAXN];
void CreateBTNode(BTNode *&b)
{
    BTNode *St[MAXN],*p;
    int top=-1,k,l=strlen(str);
    b=NULL;
    for(int i=0; i<l; ++i)
        switch(str[i])
        {
        case '(':
            St[++top]=p;
            k=1;
            break;
        case ')':
            --top;
            break;
        case ',':
            k=2;
            break;
        default:
            p=(BTNode *)malloc(sizeof(BTNode));
            int j=0;
            while(i<l&&str[i]!='('&&str[i]!=')'&&str[i]!=',')
                p->data[j++]=str[i++];
            --i;
            p->data[j]='\0';
            p->lchild=p->rchild=NULL;
            if(!b)
                b=p;
            else
                switch(k)
                {
                case 1:
                    St[top]->lchild=p;
                    break;
                case 2:
                    St[top]->rchild=p;
                    break;
                }
        }
}
bool Symm(BTNode *b1,BTNode *b2)
{
    if(!b1&&!b2)
        return true;
    if(!b1||!b2)
        return false;
    if(strcmp(b1->data,b2->data)!=0)
        return false;
    return Symm(b1->lchild,b2->rchild)&&Symm(b1->rchild,b2->lchild);
}
bool Symmtree(BTNode *b)
{
    if(!b)
        return true;
    return Symm(b->lchild,b->rchild);
}
void DestroyBT(BTNode *&b)
{
    if(b->lchild)
        DestroyBT(b->lchild);
    if(b->rchild)
        DestroyBT(b->rchild);
    free(b);
}
int main()
{
    while(~scanf("%s",str))
    {
        BTNode *b;
        CreateBTNode(b);
        puts(Symmtree(b)?"YES":"NO");
        DestroyBT(b);
    }
}

  B. 拮据的模拟城市

  这道题的题意其实非常明确,就是要求最小生成树的各边权值和。唯一要注意的是图中可以有重边,所以使用邻接矩阵建图时需要注意。

  这里我给出kruskal和prim两种做法的标程。

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1005;
const int MAXM=100005;
struct edge
{
    int u,v,w;
    edge(int _u=0,int _v=0,int _w=0):u(_u),v(_v),w(_w) {}
    bool operator<(const edge &oth) const
    {
        return w<oth.w;
    }
} e[MAXM];
int n,m;
int 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);
}
int kruskal()
{
    sort(e,e+m);
    int ret=0,cnt=0;
    init();
    for(int i=0; cnt<n-1&&i<m; ++i)
        if(find(e[i].u)!=find(e[i].v))
        {
            merge(e[i].u,e[i].v);
            ret+=e[i].w;
            ++cnt;
        }
    return ret;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<m; ++i)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        printf("%d\n",kruskal());
    }
}
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=1005;
const int INF=0x3f3f3f3f;
int g[MAXN][MAXN],n,lowc[MAXN];
int prim()
{
    for(int i=1; i<=n; ++i)
        lowc[i]=g[1][i];
    int ret=0;
    for(int i=1; i<n; ++i)
    {
        int mark=-1,minc=INF;
        for(int j=1; j<=n; ++j)
            if(lowc[j]&&minc>lowc[j])
            {
                minc=lowc[j];
                mark=j;
            }
        ret+=minc;
        lowc[mark]=0;
        for(int j=1; j<=n; ++j)
            if(lowc[j]&&lowc[j]>g[mark][j])
                lowc[j]=g[mark][j];
    }
    return ret;
}
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);
            if(l<g[u][v])
                g[u][v]=g[v][u]=l;
        }
        printf("%d\n",prim());
    }
}

  C. 迷宫里有一只薛定谔的猫!

  这道题要求的,是从1点到其余所有点的最短距离的期望。所以跑一遍单源最短路就可以了,要注意的依然是重边问题。

  关于单源最短路,书中只给出了dijkstra算法,事实上这不是唯一的单源最短路算法。我这里给出dijkstra+邻接矩阵和spfa+邻接表的两种标程。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=505;
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=0; 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,a,b,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",&a,&b,&l);
            g[a][b]=min(g[a][b],l);
        }
        dijkstra(1);
        double ans=0;
        for(int i=1; i<=n; ++i)
            ans+=dist[i];
        printf("%.2f\n",ans/(n-1));
    }
}
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int MAXN=505;
const int MAXM=MAXN*MAXN;
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;
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 u=q.front();
        q.pop();
        inque[u]=false;
        for(int i=g.head[u]; ~i; i=g.next[i])
        {
            int v=g.to[i];
            if(dist[u]+g.len[i]<dist[v])
            {
                dist[v]=dist[u]+g.len[i];
                if(!inque[v])
                {
                    q.push(v);
                    inque[v]=true;
                }
            }
        }
    }
}
int main()
{
    int n,m,a,b,l;
    while(~scanf("%d%d",&n,&m))
    {
        g.init();
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&l);
            g.add(a,b,l);
        }
        spfa(1);
        double ans=0;
        for(int i=1; i<=n; ++i)
            ans+=dist[i];
        printf("%.2f\n",ans/(n-1));
    }
}

  另外说一句,设V是点数,E是边数;标准的dijkstra算法,使用邻接矩阵,复杂度是O(V^2)的,使用堆优化或者优先队列优化,再使用恰当的建图方式,可以有效降低复杂度。标准spfa算法,使用邻接表,复杂度大概是O(k*E),k根据图的不同,可能很小,也可能大到接近V,用某些优化方法可以使k减小。换言之,应该根据图的特性(稠密图?稀疏图?)来选择最合适的算法。

  D. 迷宫里有两只薛定谔的猫!

  这道题是求任意两点之间最短距离的期望。或许因为和上一题太像,许多人选择在每一个点上都跑一次dijkstra算法。方法上没有问题,但为什么不使用floyd算法呢……在多源最短路的问题上,floyd算法显然代码更短,效率更高。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=205;
const int INF=0x3f3f3f3f;
int g[MAXN][MAXN],n;
void floyd()
{
    for(int k=1; k<=n; ++k)
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
                g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
int main()
{
    int m,a,b,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",&a,&b,&l);
            if(l<g[a][b])
                g[a][b]=g[b][a]=l;
        }
        floyd();
        double ans=0;
        for(int i=1; i<=n; ++i)
            for(int j=1; j<=n; ++j)
                ans+=g[i][j];
        printf("%.2f\n",ans/(n*(n-1)));
    }
}

  E. 迷宫里有一只深井冰!

  这道题,大概是渣诚满满的恶意……这是一道基础的TSP旅行商问题,是一个NP问题,这也意味着它并没有多项式复杂度的解法,所以不属于各种最短路算法的范畴;从至多10个点的数据量大概也可以看出,我们只是想让大家写一发暴搜。

  不过暴力也是有很多方法的,我的做法相当于取了个巧;假设从0点出发,用next_permutation()函数穷举出所有以0开头的排列,然后计算这种排列代表的走法的路径长度,取最短的即可。这样暴力的复杂度是O(n!)。

  另外,这道题是可以状压dp的,可以把复杂度降到O(n^2*2^n),有兴趣的童鞋可以研究一下。简单地说,设从0点出发,dp(i,j)表示在状态i下访问到j点的最短路径长度,则dp((1<<n)-1,k)就表示遍历过所有点且以k点为终点的最短路径长度。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int g[10][10],s[10];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=0; i<n; ++i)
            for(int j=0; j<n; ++j)
                scanf("%d",&g[i][j]);
        for(int i=0; i<n; ++i)
            s[i]=i;
        int ans=INF;
        while(s[0]==0)
        {
            int tmp=g[s[n-1]][s[0]];
            for(int i=1; i<n; ++i)
                tmp+=g[s[i-1]][s[i]];
            ans=min(ans,tmp);
            next_permutation(s,s+n);
        }
        printf("%d\n",ans);
    }
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int g[10][10],dp[10][1<<10];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=0; i<n; ++i)
            for(int j=0; j<n; ++j)
                scanf("%d",&g[i][j]);
        memset(dp,0x3f,sizeof(dp));
        dp[0][1]=0;
        for(int i=1; i<(1<<n); i+=2)
            for(int j=0; j<n; ++j)
                if(i&(1<<j))
                    for(int k=0; k<n; ++k)
                        if(!(i&(1<<k)))
                            dp[k][i|(1<<k)]=min(dp[k][i|(1<<k)],dp[j][i]+g[j][k]);
        int ans=INF;
        for(int i=1; i<n; ++i)
            ans=min(ans,dp[i][(1<<n)-1]+g[i][0]);
        printf("%d\n",ans);
    }
}

  F. 结点距离下集

  这是一道陈题,也是我觉得出题有失误的一道题。倒不是因为这道题所需知识超出了范围,而是这道题的题目描述相当不清晰。题目实际是在描述这样一个图,首先这个图是一个森林(对,没有环),在森林中的每棵树都有这样的特点,就是其中有一个节点是根节点(即题目中的集换中心),而其它节点的度(双向边,无所谓入度出度)小于等于2,形象一点地说,就是每一棵树都像神经元一样,根节点是细胞体,其余节点都在各条突触上,且每条突触不会分叉。

  所以,在当前的知识储备下,做法就是先遍历整个森林,找到每棵树上的根节点(统计度数就可以了,如果所有节点的度数都小于等于2,则这棵树必定是一条链,可以任选一个节点作为根节点);然后,再从根节点出发,对每棵树进行遍历,并记录每个节点的深度,和所在链的编号。这样,任意一棵树中的两个节点,倘若在一条链上,其最小距离就是深度的差,倘若不在一条链上,其距离就是深度的和(因为要通过根节点)。放一下渣诚的标程作为参考。

//trashLHC

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#define MAXV 100001
int a[MAXV],b[MAXV];
typedef struct ANode
{
   int adjvex;
   struct ANode *nextarc;
   int info;
   int router;
}ArcNode;
typedef struct Vnode
{
        int data;
        ArcNode *firstarc;
}VNode;
typedef VNode AdjList[MAXV];
typedef struct
{
   AdjList adjlist;
   int n,e;
}ALGraph;
int visited[MAXV];
void CreateList(ALGraph *&G,int n,int l)
{
   int i,j;
   ArcNode *p,*q;
   G=(ALGraph *)malloc(sizeof(ALGraph));
   for(i=0;i<=n;i++)
      G->adjlist[i].firstarc=NULL;
   for(i=0;i<l;i++)
   {
            p=(ArcNode *)malloc(sizeof(ArcNode));
            p->adjvex=b[i];
            p->nextarc=G->adjlist[a[i]].firstarc;
            G->adjlist[a[i]].firstarc=p;
            q=(ArcNode *)malloc(sizeof(ArcNode));
            q->adjvex=a[i];
            q->nextarc=G->adjlist[b[i]].firstarc;
            G->adjlist[b[i]].firstarc=q;
   }
   G->n=n;G->e=l;
}
int route=0;
int length=0;
int distance[MAXV];
int rout[MAXV];
void DFS(ALGraph *G,int v)
{
   ArcNode *p,*q;
   visited[v]=1;
   p=G->adjlist[v].firstarc;
   p->router=route;
   p->info=length;
   distance[v]=p->info;
   rout[v]=p->router;
   while(p!=NULL)
   {
      if(!visited[p->adjvex])
         break;
      p=p->nextarc;
   }
   if(p==NULL)
   {
     route++;
     length=0;
   }
   p=G->adjlist[v].firstarc;
   while(p!=NULL)
   {
      if(!visited[p->adjvex])
      {
         length++;
         DFS(G,p->adjvex);
      }
      p=p->nextarc;
   }
}

void DetroyGraph(ALGraph *&G)
{
    ArcNode *p,*q;
    for(int i=0;i<=G->n;i++)
    {
       p=G->adjlist[i].firstarc;
       while(p!=NULL)
       {
           q=p;
           p=p->nextarc;
           free(q);
       }
    }
    free(G);
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
       int m,n;
       scanf("%d%d",&m,&n);
       ALGraph *G2;
       int max=0;
       int counter[MAXV];
       for(int i=0;i<=m;i++)
       {
           counter[i]=0;
           distance[i]=0;
           rout[i]=0;
           visited[i]=0;
       }
       for(int i=0;i<m;i++)
       {
          scanf("%d%d",&a[i],&b[i]);
          counter[a[i]]++;counter[b[i]]++;
       }
       int recorder=0;
       for(int i=0;i<=m;i++)
          if(max<counter[i])
          {
                    recorder=i;
                    max=counter[i];
          }
       CreateList(G2,m,m);
       DFS(G2,recorder);
      for(int i=0;i<n;i++)
      {
         int temp1,temp2;
         scanf("%d%d",&temp1,&temp2);
         if(rout[temp1]==rout[temp2])printf("%d\n",abs(distance[temp1]-distance[temp2]));
         else                       printf("%d\n",distance[temp1]+distance[temp2]);
      }
       route=0;
       length=0;
      DetroyGraph(G2);
    }
}

  而我不是这么做的,我的做法超出了课程范围,不过适用范围会更广一些,只要保证图是一个森林就可以了,对每棵树的具体形状并没有要求,这里简单说一下供有兴趣的童鞋探究。我的做法是处理出树上任意两点的lca,即最近公共祖先,则这两点的距离就是这两点的深度之和减去2×最近公共祖先的深度。处理lca的方法有两种,一种是在线倍增法,复杂度是O(nlogn),一种是离线Tarjan算法,复杂度上O(n)。下面分别放出两种做法的标程。

//by wjfwzzc
//lca+倍增

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=100005;
const int MAXM=200005;
const int maxd=17;
struct graph
{
    int head[MAXN];
    int to[MAXM];
    int next[MAXM];
    int tot;
    void init()
    {
        tot=0;
        memset(head,0xff,sizeof(head));
    }
    void add(int u,int v)
    {
        to[tot]=v;
        next[tot]=head[u];
        head[u]=tot++;
    }
} g;
int d[MAXN],f[MAXN][maxd];
void dfs(int u,int fa)
{
    f[u][0]=fa;
    for(int i=1; i<maxd; ++i)
        f[u][i]=f[f[u][i-1]][i-1];
    for(int i=g.head[u]; ~i; i=g.next[i])
    {
        int v=g.to[i];
        if(v!=fa)
        {
            d[v]=d[u]+1;
            dfs(v,u);
        }
    }
}
int lca(int u,int v)
{
    if(d[u]<d[v])
        swap(u,v);
    int k=d[u]-d[v];
    for(int i=0; i<maxd; ++i)
        if((1<<i)&k)
            u=f[u][i];
    if(u==v)
        return u;
    for(int i=maxd-1; i>=0; --i)
        if(f[u][i]!=f[v][i])
        {
            u=f[u][i];
            v=f[v][i];
        }
    return f[u][0];
}
int main()
{
    int t,m,n,a,b;
    scanf("%d",&t);
    while(t--)
    {
        g.init();
        scanf("%d%d",&m,&n);
        while(m--)
        {
            scanf("%d%d",&a,&b);
            g.add(a,b);
            g.add(b,a);
        }
        d[0]=0;
        dfs(0,-1);
        while(n--)
        {
            scanf("%d%d",&a,&b);
            printf("%d\n",d[a]+d[b]-2*d[lca(a,b)]);
        }
    }
}
//by wjfwzzc
//lca+Tarjan

#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=100005;
const int MAXM=200005;
struct graph
{
    int head[MAXN];
    int to[MAXM];
    int idx[MAXM];
    int next[MAXM];
    int tot;
    void init()
    {
        tot=0;
        memset(head,0xff,sizeof(head));
    }
    void add(int u,int v,int w=-1)
    {
        to[tot]=v;
        idx[tot]=w;
        next[tot]=head[u];
        head[u]=tot++;
    }
} g,q;
int x[MAXN],y[MAXN],z[MAXN];
int f[MAXN],d[MAXN];
bool vis[MAXN];
int find(int x)
{
    if(f[x]!=x)
        return f[x]=find(f[x]);
    return f[x];
}
void tarjan(int u)
{
    vis[u]=true;
    f[u]=u;
    for(int i=q.head[u]; ~i; i=q.next[i])
    {
        int v=q.to[i];
        if(vis[v])
            z[q.idx[i]]=find(v);
    }
    for(int i=g.head[u]; ~i; i=g.next[i])
    {
        int v=g.to[i];
        if(!vis[v])
        {
            d[v]=d[u]+1;
            tarjan(v);
            f[v]=u;
        }
    }
}
int main()
{
    int t,m,n,a,b;
    scanf("%d",&t);
    while(t--)
    {
        g.init();
        scanf("%d%d",&m,&n);
        while(m--)
        {
            scanf("%d%d",&a,&b);
            g.add(a,b);
            g.add(b,a);
        }
        q.init();
        for(int i=1; i<=n; ++i)
        {
            scanf("%d%d",&x[i],&y[i]);
            q.add(x[i],y[i],i);
            q.add(y[i],x[i],i);
        }
        memset(vis,false,sizeof(vis));
        d[0]=0;
        tarjan(0);
        for(int i=1; i<=n; ++i)
            printf("%d\n",d[x[i]]+d[y[i]]-2*d[z[i]]);
    }
}

  G. Fibonacci Tree

  这是我在2013年ACM成都现场赛做过的原题,网上自然有很多题解;但有十余位童鞋选择直接照搬网上代码还是很令人寒心。这道题是说权值只有1和0两种的一个图,问其是否存在权值和为Fibonacci数的生成树。直接做一遍最小生成树和最大生成树(最小生成树的两种算法本质都是贪心,所以可以很容易地改写出求最大生成树),然后注意到,我们假设把得到的最小生成树逐边修改得到最大生成树,在修改的过程中,必然会出现把0边改成1边这样的情况,换言之,最小生成树和最大生成树的权值之间形成的区间,一定是致密的,再换种说法,这个区间内任何一个值对应的生成树必然存在;所以,我们只要判断是否有Fibonacci数存在于这个区间内就可以了。所以最后的做法是预处理出一些Fibonacci数(30个左右即可),然后在求出最小生成树和最大生成树之后,穷举Fibonacci数进行判断即可。这里给出使用kruskal和prim两种算法的标程。

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=1005;
const int MAXM=100005;
struct edge
{
    int u,v,w;
    edge(int _u=0,int _v=0,int _w=0):u(_u),v(_v),w(_w) {}
} e[MAXM];
bool cmp1(const edge &a,const edge &b)
{
    return a.w<b.w;
}
bool cmp2(const edge &a,const edge &b)
{
    return a.w>b.w;
}
bool (*cmp[])(const edge&,const edge&)= {cmp1,cmp2};
int n,m;
int 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);
}
int kruskal(int k)
{
    sort(e,e+m,cmp[k]);
    int ret=0,cnt=0;
    init();
    for(int i=0; cnt<n-1&&i<m; ++i)
        if(find(e[i].u)!=find(e[i].v))
        {
            merge(e[i].u,e[i].v);
            ret+=e[i].w;
            ++cnt;
        }
    return cnt<n-1?-1:ret;
}
int main()
{
    int f[30];
    f[0]=1;
    f[1]=2;
    for(int i=2; i<30; ++i)
        f[i]=f[i-1]+f[i-2];
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=0; i<m; ++i)
            scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
        int amin=kruskal(0),amax=kruskal(1);
        bool flag=false;
        if(~amin&&~amax)
            for(int i=0; !flag&&i<30; ++i)
                if(amin<=f[i]&&amax>=f[i])
                    flag=true;
        puts(flag?"Yes":"No");
    }
}
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1005;
const int INF=0x3f3f3f3f;
bool lvis[MAXN],hvis[MAXN];
int g[MAXN][MAXN][2],n,lc[MAXN],hc[MAXN],amin,amax;
void prim()
{
    memset(lvis,false,sizeof(lvis));
    memset(hvis,false,sizeof(hvis));
    for(int i=1; i<=n; ++i)
    {
        lc[i]=g[1][i][0];
        hc[i]=g[1][i][1];
    }
    lvis[1]=hvis[1]=true;
    amin=amax=0;
    for(int i=1; i<n; ++i)
    {
        int lmark=-1,hmark=-1,lminc=INF,hminc=-1;
        for(int j=1; j<=n; ++j)
        {
            if(!lvis[j]&&lminc>lc[j])
            {
                lminc=lc[j];
                lmark=j;
            }
            if(!hvis[j]&&hminc<hc[j])
            {
                hminc=hc[j];
                hmark=j;
            }
        }
        if(!~lmark)
        {
            amin=-1;
            break;
        }
        if(!~hmark)
        {
            amax=-1;
            break;
        }
        lvis[lmark]=hvis[hmark]=true;
        amin+=lc[lmark];
        amax+=hc[hmark];
        for(int j=1; j<=n; ++j)
        {
            if(!lvis[j]&&lc[j]>g[lmark][j][0])
                lc[j]=g[lmark][j][0];
            if(!hvis[j]&&hc[j]<g[hmark][j][1])
                hc[j]=g[hmark][j][1];
        }
    }
}
int main()
{
    int f[30];
    f[0]=1;
    f[1]=2;
    for(int i=2; i<30; ++i)
        f[i]=f[i-1]+f[i-2];
    int m,a,b,c;
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1; i<=n; ++i)
        {
            for(int j=1; j<=n; ++j)
            {
                g[i][j][0]=INF;
                g[i][j][1]=-1;
            }
            g[i][i][0]=g[i][i][1]=0;
        }
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            if(c<g[a][b][0])
                g[a][b][0]=g[b][a][0]=c;
            if(c>g[a][b][1])
                g[a][b][1]=g[b][a][1]=c;
        }
        prim();
        bool flag=false;
        if(~amin&&~amax)
            for(int i=0; !flag&&i<30; ++i)
                if(amin<=f[i]&&amax>=f[i])
                    flag=true;
        puts(flag?"Yes":"No");
    }
}

  最后我想说,上机愈少,期末临近,且练且珍惜。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值