第一题
简单题意
给出一棵树,找出树上所有点的能到达的最远距离。输入格式第一行一个N,表示有N个节点,接下来N-1行,每行一个v,w,第i行表示i和v之间有一条长度为w的边。
思路
首先找出树的直径,然后对于树上的一点,它到直径的两个端点的距离中的最大距离就是到这棵树的所有点中的最大距离。设直径为ab,然后对于一点c,设ac比bc大,则ac就是所求答案,我们可以设d,假设cd>ac,cd与ac相交于一点e,必然相交,最差也相交于c,则ed>ad,那么直径就是be而不是ab了,所以不存在d使得cd>ac。
寻找直径可以直接使用dfs将树走一遍。
代码
#include<iostream>
#include<vector>
using namespace std;
struct edge
{
int v,w;
};
vector<edge> a[10010];
int dis[10010],maxl,s;
void dfs(int u,int from,int l)
{
if (maxl<l)
{
s=u;
maxl=l;
}
for (int i=0;i<a[u].size();i++)
{
int v,w;
v=a[u][i].v;
w=a[u][i].w;
if (from!=v)
{
dfs(v,u,l+w);
dis[v]=max(dis[v],l+w);
}
}
return;
}
int main()
{
int n,v,w;
edge x;
while (cin>>n)
{
for (int i=0;i<=n;i++)
dis[i]=0;
for (int i=0;i<=n;i++)
a[i].clear();
for (int i=2;i<=n;i++)
{
cin>>v>>w;
x.v=v; x.w=w;
a[i].push_back(x);
x.v=i; x.w=w;
a[v].push_back(x);
}
s=0; maxl=0;
dfs(1,0,0);
dfs(s,0,0);
dfs(s,0,0);
for (int i=1;i<=n;i++)
cout<<dis[i]<<endl;
}
return 0;
}
第二题
简单题意
给出n,m,表示有n个学生,m个团体,现在0号学生患病,只要在一个团体内有患病的,就视为整个团体全部患病,求多少学生要被隔离。
输入格式为第一行n,m,接下来m行,每行第一个为sum,接下来sum个编号,表示该编号学生被感染。多组数据,n,m都为0时表示结束,
输出要隔离的人数。
思路
开始设每个学生都是独立的,打一个标记f[i]=i,每读入一个团体,团体中所有学生的f[i]改为团体中编号最小的学生,改变过程定义一个find函数,直接改变走过的所有学生。
代码
#include<iostream>
using namespace std;
int f[50050],a[50050];
int find(int x)
{
if (x==f[x])
return x;
f[x]=find(f[x]);
return f[x];
}
int main()
{
int n,m,sum,x,y,min,ans;
cin>>n>>m;
while (n!=0 || m!=0)
{
for (int i=0;i<n;i++)
f[i]=i;
for (int i=0;i<m;i++)
{
cin>>sum;
min=n;
for (int j=0;j<sum;j++)
{
cin>>a[j];
if (a[j]<min)
min=a[j];
}
for (int j=0;j<sum;j++)
if (a[j]!=min)
f[find(a[j])]=find(min);
}
x=find(0);
ans=0;
for (int i=0;i<n;i++)
if (find(i)==x)
ans++;
cout<<ans<<endl;
cin>>n>>m;
}
return 0;
}
第三题
简单题意
给出n块农田,要求全部灌溉,有两种灌溉方式,一是黄河之水天上来,直接花费wi灌溉,wi给出,一是建立传送门,连接两块农田,水通过传送门流通。花费Pij。求出最小花费。
输入格式为第一行n,表示n块农田,接下来n行,表示wi,接下来n行,表示Pij。
思路
直接新增一个0号节点,这样就成了一个n+1的最小生成树。
最小生成树的构建,将所有的边按照权值排序,每次取最小的边,看是否能加入到树中,即该边的两个节点是否已经相连,若不相连则可以加入,将两个节点连接。边权计入ans。
代码
#include<iostream>
#include<algorithm>
using namespace std;
struct edge
{
int u,v,w;
bool operator <(const edge &x)
{
return w<x.w;
}
};
edge a[500500];
int f[500500];
int find(int x)
{
if (x==f[x])
return f[x];
f[x]=find(f[x]);
return f[x];
}
int main()
{
int n,tot,x,y,ans,k,l;
cin>>n;
tot=0;
for (int i=1;i<=n;i++)
{
tot++;
a[tot].u=0;
a[tot].v=i;
cin>>a[tot].w;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
cin>>x;
if (i<j)
{
tot++;
a[tot].u=i;
a[tot].v=j;
a[tot].w=x;
}
}
sort(a,a+tot+1);
for (int i=0;i<=n;i++)
f[i]=i;
ans=0;
for (int l=1;l<=tot;l++)
{
x=find(a[l].u);
y=find(a[l].v);
if (x!=y)
{
ans=ans+a[l].w;
f[x]=y;
}
}
cout<<ans<<endl;
return 0;
}
第四题
简单题意
求一个最小生成树的最大边权是多少。
思路
最小生成树同第三题,最大边权只需要在加入边时记录比较一下即可。
代码
#include<iostream>
#include<algorithm>
using namespace std;
struct edge
{
int u,v,w;
bool operator <(const edge &x)
{
return w<x.w;
}
};
edge a[100010];
int f[50050];
int find(int x)
{
if (x==f[x])
return f[x];
f[x]=find(f[x]);
return f[x];
}
int main()
{
int n,m,root,max;
cin>>n>>m>>root;
for (int i=0;i<m;i++)
cin>>a[i].u>>a[i].v>>a[i].w;
sort(a,a+m);
for (int i=0;i<=n;i++)
f[i]=i;
max=0;
for (int i=0;i<m;i++)
if (find(a[i].u)!=find(a[i].v))
{
f[find(a[i].u)]=find(a[i].v);
if (a[i].w>max) max=a[i].w;
}
cout<<max<<endl;
return 0;
}
第五题
简单题意
一共五张牌,有九种牌面,已经给了你两张,让你从剩余的牌中自己随便选三种,然后求中组成这九种牌面的方案数。
扑克牌的大小和花色是给出的,第一行输入A和B,表示这副牌大小为A,花色有B种。
输出为一行九个整数,表示九种牌型的方案数。
思路
数学题,对于同花顺和顺子,判断已有两张牌的大小和花色,同花,从剩余的n-2中任选三张-同花顺,炸弹,先构成一个炸弹,两张牌大小相同则那剩余两张并任选一张,否则那三张相同的,之后也是这样,详情看代码。
代码
#include<iostream>
#include<cstdlib>
using namespace std;
int ans[10]={0};
int C(int n,int m)
{
int ans=1;
for (int i=n;i>n-m;i--)
ans=ans*i;
for (int i=1;i<=m;i++)
ans=ans/i;
return ans;
}
int main()
{
int n,m,x1,y1,x2,y2,l;
cin>>n>>m;
cin>>x1>>y1>>x2>>y2;
if (x1>x2)
{
l=x1; x1=x2; x2=l;
l=y1; y1=y2; y2=l;
}
l=5-x2+x1;
if (x1-n+5>0) l=l-(x1-n+5);
if (4-x2>0) l=l-(4-x2);
if (l>0 && x1!=x2 && y1==y2)
ans[1]=l; //1
l=l*m*m*m-ans[1];
if (l>0 && x1!=x2)
{
ans[2]=l; //2
l=0;
}
if (x1!=x2 && y1==y2)
ans[3]=C(n-2,3)-ans[1]; //3
if (m==4)
{
if (x1==x2) ans[4]=n-1;
else ans[4]=2; //4
}
if (m>=3)
{
if (x1==x2) ans[5]=C(m,3)*(n-1)+C(m-2,1)*C(m,2)*(n-1); //5
else ans[5]=2*C(m-1,2)*C(m-1,1);
}
if (m>=2)
{
if (x1==x2) ans[6]=C(n-1,1)*C(m,2)*C(n-2,1)*C(m,1); //6
else ans[6]=(m-1)*(m-1)*(n-2)*m+2*(m-1)*(n-2)*C(m,2);
}
if (m>=3)
{
if (x1==x2) ans[7]=C(m-2,1)*C(n-1,2)*m; //7
else ans[7]=C(n-2,1)*C(m,3)+2*C(n-2,1)*m*C(m-1,2);
}
if (x1!=x2)
ans[9]=C(n-2,3)*m*m*m-ans[1]-ans[2]-ans[3]; //9
ans[8]=C(n*m-2,3);
for (int i=1;i<=9;i++)
if (i!=8)
ans[8]=ans[8]-ans[i]; //8
for (int i=1;i<10;i++)
cout<<ans[i]<<" ";
cout<<endl;
return 0;
}