这次上机的题目考查的内容以图论的一些基础算法为主,我实话实话这次上机题有出失误的地方,个别题的难度没有控制好,所以过题情况不佳也是情理之中。
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");
}
}
最后我想说,上机愈少,期末临近,且练且珍惜。