Week10
图论2
10-1 Einstein学画画
题目链接
解题思路
一笔画、欧拉路板子题,存在欧拉路(即可以一笔画)的条件:图联通,且存在0或2个奇点(顶点度数为奇数)。一个连通图中,奇点个数必然为偶数。若奇点数大于2,可以想到最优解就是每次划掉两个奇点,这样结果必然就是ans/2。要注意ans=0时需要特判一下,因为至少需要一笔。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5,maxm=1e5+5;
int cnt[maxn];
int main(){
int n,m;
cin>>n>>m;
for (int i=1; i<=m; i++){
int u,v;
scanf("%d%d",&u,&v);
cnt[u]++;
cnt[v]++;
}
int ans=0;
for (int i=1; i<=n; i++){
if (cnt[i]%2){
ans++;
}
}
if (!ans){
cout<<1;
}
else {
cout<<ans/2;
}
return 0;
}
10-2 合根植物
题目链接
解题思路
并查集思路,最后扫描的时候寻找每一个集合的父结点个数即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+100;
int m,n,k;
int f[maxn];
int b[maxn];
int find(int k){
if (k==f[k]){
return k;
}
else
return f[k]=find(f[k]);
}
int main(){
cin>>m>>n;
int cnt=0;
for (int i=1; i<=m; i++){
for (int j=1; j<=n; j++){
cnt++;
f[cnt]=cnt;
}
}
cin>>k;
for (int i=1; i<=k; i++){
int u,v;
scanf("%d%d",&u,&v);
f[find(u)]=find(v);
}
int ans=0;
cnt=0;
for (int i=1; i<=m; i++){
for (int j=1; j<=n; j++){
cnt++;
if (f[cnt]==cnt){
ans++;
}
}
}
cout<<ans;
return 0;
}
10-3 奶酪
题目链接
解题思路
并查集,需要特殊判断一个集合能否同时碰到顶和底:可以在读入的时候就进行处理,如果z坐标满足条件,则相应的top[ ]或者bott[ ]数组就加一,最后扫描的时候只要判断top[ ]数组和bott[ ]数组中是否存在一个共同集合即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e3+5;
ll T,n,h,r;
struct node{
ll x,y,z;
};
node a[maxn];
ll f[maxn];
ll find(ll k){
if (k==f[k]){
return k;
}
return f[k]=find(f[k]);
}
bool doit(ll i,ll j){
if (sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+
(a[i].y-a[j].y)*(a[i].y-a[j].y)+
(a[i].z-a[j].z)*(a[i].z-a[j].z)) <= 2*r){
return true;
}
else {
return false;
}
}
int top[maxn],bott[maxn];
int top1,bott1;
int main(){
// freopen("LGP3958奶酪.in","r",stdin);
// freopen("LGP3958奶酪.out","w",stdout);
cin>>T;
while (T--){
top1=0;
bott1=0;
scanf("%d%d%d",&n,&h,&r);
for (ll i=1; i<=n; i++){
f[i]=i;
}
for (ll i=1; i<=n; i++){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
if (abs(h-a[i].z)<=r){
top[++top1]=i;
}
if (abs(a[i].z)<=r){
bott[++bott1]=i;
}
}
for (ll i=1; i<=n; i++){
for (ll j=i+1; j<=n; j++){
if (doit(i,j)){
f[find(i)]=find(j);
}
}
}
bool jud=0;
for (int i=1; i<=bott1; i++){
for (int j=1; j<=top1; j++){
if (find(bott[i])==find(top[j])){
jud=1;
cout<<"Yes"<<endl;
break;
}
}
if (jud){
break;
}
}
if (!jud){
cout<<"No"<<endl;
}
}
return 0;
}
10-4 灾后重建
题目链接
解题思路
由N<=200,并结合题面发现每一次操作都符合floyd思想,所以用floyd。在每一次询问的时候,就不断更新now,使得now指向符合要求的最大节点,这样f[ ][ ]就指向了当前最短路径。需要特别注意,因为floyd算法的本性(只能判断起始节点和终止结点之间不能经过的结点大小,而不能判断起始节点和终止结点本身能否符合要求的大小),所以最后一定要有t[u]>time || t[v]>time的判断。
#include<bits/stdc++.h>
using namespace std;
const int maxn=205,maxm=2e4+10;
int n,m,q;
int t[maxn];
int f[maxn][maxn];
#define MAXN 0x3f3f3f3f
int main(){
cin>>n>>m;
for (int i=0; i<n; i++){
scanf("%d",&t[i]);
}
memset(f,0x3f,sizeof(f));
for (int i=1; i<=n; i++){
f[i][i]=0;
}
for (int i=1; i<=m; i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
int p=max(u,v);
f[u][v]=min(f[u][v],w);
f[v][u]=min(f[v][u],w);
}
int now=-1;
cin>>q;
for (int i=1; i<=q; i++){
int u,v,time;
scanf("%d%d%d",&u,&v,&time);
while (t[now+1]<=time && now+1<n){
now++;
for (int i=0; i<n; i++){
for (int j=0; j<n; j++){
f[i][j]=min(f[i][j],f[i][now]+f[now][j]);
}
}
}
if (f[u][v]==MAXN || t[u]>time || t[v]>time){
cout<<-1<<endl;
}
else {
cout<<f[u][v]<<endl;
}
}
return 0;
}
10-5 聪明的猴子
题目链接
解题思路
用Kruskal处理最小生成树,算出任意两个结点之间的距离,构造出所有边数,然后Kruskal中记录下所需边中最大的权值。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+5,maxm=510,maxn_2=1e6+5;
int n,m;
int t[maxm],f[maxn];
struct node{
int x,y;
double s;
}a[maxn_2];
bool cmp(node a,node b){
return a.s<b.s;
}
struct edge{
int u,v;
}atmp[maxn_2];
int find(int k){
if (k==f[k]){
return k;
}
return f[k]=find(f[k]);
}
int main(){
cin>>m;
for (int i=1; i<=m; i++){
cin>>t[i];
}
cin>>n;
for (int i=1; i<=n; i++){
cin>>atmp[i].u>>atmp[i].v;
}
int cnte=0;
for (int i=1; i<=n; i++){
for (int j=i+1; j<=n; j++){
cnte++;
a[cnte].x=i;
a[cnte].y=j;
a[cnte].s=sqrt((atmp[i].u-atmp[j].u)*(atmp[i].u-atmp[j].u)+(atmp[i].v-atmp[j].v)*(atmp[i].v-atmp[j].v));
}
}
sort(a+1,a+1+cnte,cmp);
double ans=0;
for (int i=1; i<=n; i++){
f[i]=i;
}
int tmp=0;
for (int i=1; i<=cnte; i++){
int u=find(a[i].x);
int v=find(a[i].y);
if (u==v){
continue;
}
f[u]=v;
tmp++;
ans=max(ans,a[i].s);
if (tmp==n-1){
break;
}
}
int cnt=0;
for (int i=1; i<=m; i++){
if (ans<=t[i]){
cnt++;
}
}
cout<<cnt;
return 0;
}
*10-6 【模板】最小生成树
题目链接
解题思路
Kruskal算法
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e3+5,maxm=2e5+5;
int n,m;
struct node{
int u,v,w;
}a[maxm];
bool cmp(node a,node b){
return a.w<b.w;
}
int f[maxn],ans,cnt;
int find(int k){
if (k==f[k]){
return k;
}
return f[k]=find(f[k]);
}
int main(){
cin>>n>>m;
for (int i=1; i<=m; i++){
int u,v,w;
scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
}
sort(a+1,a+1+m,cmp);
for (int i=1; i<=n; i++){
f[i]=i;
}
for (int i=1; i<=m; i++){
int eu=find(a[i].u),ev=find(a[i].v);
if (eu==ev){
continue;
}
f[eu]=ev;
ans+=a[i].w;
cnt++;
if (cnt==n-1){
break;
}
}
if (cnt==n-1){
cout<<ans;
}
else {
cout<<"orz";
}
return 0;
}