P1636 Einstein学画画
题目描述
Einstein 学起了画画。
此人比较懒~~,他希望用最少的笔画画出一张画……
给定一个无向图,包含 nn 个顶点(编号 1 \sim n1∼n),mm 条边,求最少用多少笔可以画出图中所有的边。
输入格式
第一行两个整数 n, mn,m。
接下来 mm 行,每行两个数 a, ba,b(a \ne ba=b),表示 a, ba,b 两点之间有一条边相连。
一条边不会被描述多次。
输出格式
一个数,即问题的答案。
连通的图奇点<=2可以一笔画完。
奇点:与奇数条边相关联的点。
奇点只能是偶数,证明:(12条消息) 图中奇点数量不可能有奇数个的证明_yihanyifan的博客-CSDN博客_不可能有偶数个节点奇数条边(我不会证明...)
#include <bits/stdc++.h>
#include <cstdio>
using namespace std;
int m,n,a,b,ans;
vector< int > V[1001];
string s1,s2;
int main()
{
cin>>n>>m;
for(int i=0;i<m;i++)
{
cin>>a>>b;
V[a].push_back(b);
V[b].push_back(a);//存边
}
for(int i=1;i<=n;i++)
{
int L=V[i].size();
if(L%2==1)ans++;
}
if(ans<=2)cout<<'1';
else cout<<ans/2;
}
P8654 [蓝桥杯 2017 国 C] 合根植物
题目描述
w 星球的一个种植园,被分成 m \times nm×n 个小格子(东西方向 mm 行,南北方向 nn 列)。每个格子里种了一株合根植物。
这种植物有个特点,它的根可能会沿着南北或东西方向伸展,从而与另一个格子的植物合成为一体。
如果我们告诉你哪些小格子间出现了连根现象,你能说出这个园中一共有多少株合根植物吗?
输入格式
第一行,两个整数 mm,nn,用空格分开,表示格子的行数、列数(1<m,n<10001<m,n<1000)。
接下来一行,一个整数 kk,表示下面还有 kk 行数据 (0<k<10^5)(0<k<105)。
接下来 kk 行,第行两个整数 aa,bb,表示编号为 aa 的小格子和编号为 bb 的小格子合根了。
格子的编号一行一行,从上到下,从左到右编号。
并查集捏(110 并查集_哔哩哔哩_bilibili)
#include <bits/stdc++.h>
using namespace std;
int m,n,k,a,b,ans;
int f[1008611];//记录父节点
int deepth[1008611];
string s1,s2;
int found(int x)
{
if(f[x]==x)return x;
return f[x]=found(f[x]);//路径压缩,减少时间
}
void hb(int x,int y)
{
int a=found(x),b=found(y);
if(a==b)return;//父节点相同返回
if(deepth[a]>deepth[b])
{
f[b]=a;
deepth[b]+=deepth[a];
}
else
{
f[a]=b;
deepth[a]+=deepth[b];
}
//让x的根节点变成y的根节点
}
void hd1(int x,int y)
{
f[found(x)]=found(y);
}
int main()
{
memset(deepth,1, sizeof(deepth));
cin>>m>>n>>k;
int M=n*m;
for(int i=1;i<=M;i++)f[i]=i;
for(int i=0;i<k;i++)
{
cin>>a>>b;
hd1(a,b);
}
for(int i=1;i<=M;i++)if(found(i)==i)ans++;//找最终父节点的数量
cout<<ans;
}
P3958 [NOIP2017 提高组] 奶酪
题目背景
NOIP2017 提高组 D2T1
题目描述
现有一块大奶酪,它的高度为 hh,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系,在坐标系中,奶酪的下表面为 z = 0z=0,奶酪的上表面为 z = hz=h。
现在,奶酪的下表面有一只小老鼠 Jerry,它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交,Jerry 则可以从奶酪下表面跑进空洞;如果一个空洞与上表面相切或是相交,Jerry 则可以从空洞跑到奶酪上表面。
位于奶酪下表面的 Jerry 想知道,在不破坏奶酪的情况下,能否利用已有的空洞跑 到奶酪的上表面去?
空间内两点 P_1(x_1,y_1,z_1)P1(x1,y1,z1)、P2(x_2,y_2,z_2)P2(x2,y2,z2) 的距离公式如下:
\mathrm{dist}(P_1,P_2)=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2+(z_1-z_2)^2}dist(P1,P2)=(x1−x2)2+(y1−y2)2+(z1−z2)2
输入格式
每个输入文件包含多组数据。
第一行,包含一个正整数 TT,代表该输入文件中所含的数据组数。
接下来是 TT 组数据,每组数据的格式如下: 第一行包含三个正整数 n,h,rn,h,r,两个数之间以一个空格分开,分别代表奶酪中空洞的数量,奶酪的高度和空洞的半径。
接下来的 nn 行,每行包含三个整数 x,y,zx,y,z,两个数之间以一个空格分开,表示空洞球心坐标为 (x,y,z)(x,y,z)。
输出格式
TT 行,分别对应 TT 组数据的答案,如果在第 ii 组数据中,Jerry 能从下表面跑到上表面,则输出 Yes,如果不能,则输出 No。
用的bfs,如果底部有一个点能到底顶部结束
#include <bits/stdc++.h>
using namespace std;
double x[10005],y[10005],z[10005];
queue<int>q;
long long T,n,h,r;
int vis[10005],flag=0;
void clearqueue(queue<int>&a)//用来清空队列的函数
{
queue<int>empty;
swap(empty,a);
}
int pd(int a,int b)//判断是否相交的函数
{
double X=x[a]-x[b];
double Y=y[a]-y[b];
double Z=z[a]-z[b];
double dis=X*X+Y*Y+Z*Z;
if(dis<=4*r*r)return 1;
else return 0;
}
int main()
{
cin>>T;
while(T-->0)
{
clearqueue(q);
cin>>n>>h>>r;
for(int i=1;i<=n;i++)
{
cin>>x[i]>>y[i]>>z[i];
if(z[i]-r<=0)
{
q.push(i);//记录底部的起点
vis[i]=1;
}
}
while(!q.empty())
{
int tem=q.front();
q.pop();
if(z[tem]+r>=h)//已经到达顶部
{
flag=1;
break;
}
for(int i=1;i<=n;i++)
{
if(vis[i])continue;
if(pd(tem,i))
{
q.push(i);
vis[i]=1;
}
}
}
if(flag)cout<<"Yes"<<endl;
else cout<<"No"<<endl;
flag=0;
memset(vis,0,sizeof(vis));
}
}
P1119 灾后重建
题目背景
B 地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。
题目描述
给出 B 地区的村庄数 NN,村庄编号从 00 到 N-1N−1,和所有 MM 条公路的长度,公路是双向的。并给出第 ii 个村庄重建完成的时间 t_iti,你可以认为是同时开始重建并在第 t_iti 天重建完成,并且在当天即可通车。若 t_iti 为 00 则说明地震未对此地区造成损坏,一开始就可以通车。之后有 QQ 个询问 (x,y,t)(x,y,t),对于每个询问你要回答在第 tt 天,从村庄 xx 到村庄 yy 的最短路径长度为多少。如果无法找到从 xx 村庄到 yy 村庄的路径,经过若干个已重建完成的村庄,或者村庄 xx 或村庄 yy 在第 tt 天仍未重建完成,则需要返回 -1。
输入格式
第一行包含两个正整数N,MN,M,表示了村庄的数目与公路的数量。
第二行包含NN个非负整数t_0, t_1,…, t_{N-1}t0,t1,…,tN−1,表示了每个村庄重建完成的时间,数据保证了t_0 ≤ t_1 ≤ … ≤ t_{N-1}t0≤t1≤…≤tN−1。
接下来MM行,每行33个非负整数i, j, wi,j,w,ww为不超过1000010000的正整数,表示了有一条连接村庄ii与村庄jj的道路,长度为ww,保证i≠ji=j,且对于任意一对村庄只会存在一条道路。
接下来一行也就是M+3M+3行包含一个正整数QQ,表示QQ个询问。
接下来QQ行,每行33个非负整数x, y, tx,y,t,询问在第tt天,从村庄xx到村庄yy的最短路径长度为多少,数据保证了tt是不下降的。
输出格式
共QQ行,对每一个询问(x, y, t)(x,y,t)输出对应的答案,即在第tt天,从村庄xx到村庄yy的最短路径长度为多少。如果在第t天无法找到从xx村庄到yy村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄yy在第tt天仍未修复完成,则输出-1−1。
家人们,村庄编号是从0开始的,不是从1。
需要优化弗洛依达最外层的循环,不然就会超时。
我们记录上一次新加入的村庄k,判断村庄k与给出的修复时间t之间的大小
满足修复时间>=村庄k修复所需要的时间我们就更新一次各个村庄之间的距离。
#include <bits/stdc++.h>
using namespace std;
int M,N,Q;
int village[205],road[205][205],flag[205];
void f(int k)
{
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
road[j][i]=road[i][j]=min(road[i][j],road[i][k]+road[k][j]);
}
}
}
int main()
{
cin>>N>>M;
memset(road,0x3f3f3f,sizeof(road));
for(int i=0;i<N;i++)
{
cin>>village[i];//修复所需要的时间
road[i][i]=0;//对角线初始化
}
for(int i=1;i<=M;i++)
{
int u, v, w;
cin>>u>>v>>w;
road[u][v]=w;
road[v][u]=w;
}
cin>>Q;
int k=0;
while(Q--)
{
int x,y,t;
cin>>x>>y>>t;
if(village[x]>t||village[y]>t)
{
cout<<"-1"<<endl;
continue;
}
while(k<N)
{
if(village[k]<=t)
{
f(k);
k++;
}
else break;
}
if(road[x][y]>=0x3f3f3f)
{
cout<<-1<<endl;
continue;
}
else cout<<road[x][y]<<endl;
}
}
P2504 [HAOI2006]聪明的猴子
题目描述
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。
现在,在这个地区露出水面的有N棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)。
在这个地区住着的猴子有M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。
【问题】现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食。
输入格式
输入文件monkey.in包括:
第1行为一个整数,表示猴子的个数M(2<=M<=500);
第2行为M个整数,依次表示猴子的最大跳跃距离(每个整数值在1--1000之间);
第3行为一个整数表示树的总棵数N(2<=N<=1000);
第4行至第N+3行为N棵树的坐标(横纵坐标均为整数,范围为:-1000--1000)。
(同一行的整数间用空格分开)
输出格式
输出文件monkey.out包括一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。
题目给了一个树<=1000,但是
各个树之间会生成n^2条边,所以数组(存边的数组和记录父节点的数组)开大一些,不然会re
#include <bits/stdc++.h>
using namespace std;
struct edge
{
int u,v;
double w;
}e[10000000];//数组开大点
double monkey[505],f[1050000]/*数组开大点*/,x[1005],y[1005],m,n;
int cnt;
int find(int x)
{
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
void hb(int a,int b)
{
int j=find(a);
int l=find(b);
if(j==l)return;
else f[j]=l;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int main()
{
cin>>m;
for(int i=1;i<=m;i++)cin>>monkey[i];
cin>>n;
if(n==1)
{
cout<<m<<endl;
return 0;
}
for(int i=1;i<=n;i++)cin>>x[i]>>y[i];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(j==i)continue;
e[++cnt].u=i;
e[cnt].v= j;
e[cnt].w = sqrt((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]));
f[cnt]=cnt;//初始化父节点
}
}
sort(e+1,e+cnt+1,cmp);
double MAX=0;
int EDGE=0,asn=0;
for(int i=1;i<=cnt;i++)
{
int a=find(e[i].v);
int b=find(e[i].u);
if(a!=b)
{
EDGE++;
hb(e[i].u,e[i].v);
MAX=max(MAX,e[i].w);
}
if(EDGE+1==n)break;//边数<=点数-1(生成树而非环)
}
for(int i=1;i<=m;i++)
{
if(monkey[i]>=MAX)
{
asn++;
}
}
cout<<asn;
}