文章目录
#10064. 「一本通 3.1 例 1」黑暗城堡
没写
#10065. 「一本通 3.1 例 2」北极通讯网络
代码
#include "stdio.h"
#include "algorithm"
#include "string.h"
#include "iostream"
#include "math.h"
using namespace std;
int n,m,a[505];
double px[505],py[505];
struct ppp
{
int x,y;
double s;
bool operator <(const ppp &z)const
{
return s<z.s;
}
} p[250025];
int find(int x)
{
if(a[x]==0) a[x]=x;
if(a[x]==x) return x;
return a[x]=find(a[x]);
}
int main()
{
int n,m,l=0;
cin>>n>>m;
for(int i=1; i<=n; i++)
cin>>px[i]>>py[i];
for(int i=1; i<n; i++)
for(int j=i+1; j<=n; j++)
p[l++]= {i,j,sqrt((px[i]-px[j])*(px[i]-px[j])+(py[i]-py[j])*(py[i]-py[j]))};
sort(p,p+l);
double sum=0;
int b=0;
for(int i=0; i<l; i++)
{
if(b>=n-1-m+1)
break;
int n1=find(p[i].x);
int n2=find(p[i].y);
if(n1!=n2)
{
sum=p[i].s;
b++;
a[n1]=n2;
}
}
printf("%.2lf",sum);
}
#10066. 「一本通 3.1 练习 1」新的开始
代码
#include "stdio.h"
#include "string.h"
#include "algorithm"
using namespace std;
int a[999999+10],n;
struct ppp
{
int a,b,l;
}p[999999];
void init()
{
for(int i=0; i<=n; i++)
a[i]=i;
}
bool cmp(ppp z,ppp w)
{
return z.l<w.l;
}
int find(int z)
{
if(a[z]==z) return z;
return a[z]=find(a[z]);
}
int main()
{
int m,j,k=0,i,s=0;
scanf("%d",&n);
init();
for(i=1; i<=n; i++)
{
scanf("%d",&m);
p[k].a=0,p[k].b=i,p[k++].l=m;//很巧妙,建立发电站点与0相连
}//这道题意思是每个点只要有电就行,单看发电站我们可以知道有两个及以上的发电站用并查集写的话
//你有几个发电站,就会有几个首领(几个集合,不相交),而用了0后就很巧妙,无论你有几个发电站,
//最后都是一首领(一个集合)
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
scanf("%d",&m);
p[k].a=i,p[k].b=j,p[k++].l=m;
}
}
sort(p,p+k,cmp);
int h=0;
for(i=0; i<k; i++)
{
if(h==n)
break;
int x=find(p[i].a);
int y=find(p[i].b);
if(x!=y)
{
a[x]=y;
h++;
s+=p[i].l;
}
}
printf("%d\n",s);
}
#10067. 「一本通 3.1 练习 2」构造完全图
代码
#include "stdio.h"
#include "algorithm"
using namespace std;
#define ll long long
struct ppp
{
int u,v;
ll w;
bool operator <(const ppp &z)const
{
return w<z.w;
}
} p[100099];
int a[100099];
int dian[100099];
int find(int x)
{
if(a[x]==x) return x;
return a[x]=find(a[x]);
}
int main()
{
int n;
ll ans=0;
scanf("%d",&n);
for(int i=0; i<n-1; i++)
{
scanf("%d%d%lld",&p[i].u,&p[i].v,&p[i].w);
ans+=p[i].w;
}
sort(p,p+n-1);
for(int i=1; i<=n; i++)
{
a[i]=i;
dian[i]=1;
}
for(int i=0; i<n-1; i++)
{
int f1=find(p[i].u);
int f2=find(p[i].v);
//你扩展的边不改变原本的最小生成树的权值,又因为有且仅有一个,故p[i].w+1
ans+=(dian[f1]*dian[f2]-1)*(p[i].w+1);//dian[f1]*dian[f2]-1:减一是因为ans中已经加了p[i].u到p[i].v权值
dian[f1]+=dian[f2];//在这个集合中,同一个祖先有多少个点
a[f2]=f1;
}
printf("%lld\n",ans);
}
#10068. 「一本通 3.1 练习 3」秘密的牛奶运输
代码
#include "stdio.h"
#include "string.h"
#include "algorithm"
using namespace std;
int l=0;
int link[2999],a[509];
int ma[509][509],mi[509][509];
struct edge
{
int u,v,w;
int b;
bool operator<(const edge &e)const
{
return w<e.w;
}
} ed[10009];
struct tree
{
int v,w,next;
} t[1099];
void creat(int u,int v,int w)
{
t[l].v=v;
t[l].w=w;
t[l].next=link[u];
link[u]=l++;
}
void add(int k,int v,int u,int mmax,int mmin)
{
//k是开始的地方,像根节点·
ma[k][v]=mmax,mi[k][v]=mmin;
//ma[k][v]求的是点k到点v走过的边中哪条边最大
//mi[k][v]求的是点k到点v走过的边中哪条边第二大
for(int j=link[v];j!=-1; j=t[j].next)
{
int vv=t[j].v;
int ww=t[j].w;
if(vv!=u)
{
//注意这儿要设置变量,mmax和mmin的值不能变,
//后面返回到这一步时,mmax和mmin要的是原值
int t1=mmax,t2=mmin;
if(ww>mmax)
t2=mmax,t1=ww;
else if(ww>mmin)
t2=ww;
add(k,vv,v,t1,t2);
}
}
}
int find(int x)
{
if(a[x]==0) a[x]=x;
if(a[x]==x) return x;
return a[x]=find(a[x]);
}
int main()
{
int n,m,p=0;
scanf("%d%d",&n,&m);
long long sum=0,ans=1e18;
memset(link,-1,sizeof(link));
for(int i=0; i<m; i++)
{
ed[i].b=0;
scanf("%d%d%d",&ed[i].u,&ed[i].v,&ed[i].w);
}
sort(ed,ed+m);
for(int i=0; i<m; i++)
{
if(p==n-1)
break;
int n1=find(ed[i].u);
int n2=find(ed[i].v);
if(n1!=n2)
{
p++;
a[n1]=n2;
sum+=ed[i].w;
ed[i].b=1;
creat(ed[i].u,ed[i].v,ed[i].w);
creat(ed[i].v,ed[i].u,ed[i].w);
}
}
//通过树 求出i到其他点的路径中的最大边
for(int i=1; i<=n; i++)
add(i,i,-1,0,0);
for(int i=0; i<m; i++)
{
if(!ed[i].b)
{
int u=ed[i].u,v=ed[i].v,w=ed[i].w;
//如果这条边比u到v的路径中的最大边还大 那么可以替换 不然替换了反而变小
if(w>ma[u][v])
ans=min(ans,sum-ma[u][v]+w);
//如果和最大的边相等 那么判断是否大于次大边
else if(w>mi[u][v])
ans=min(ans,sum-mi[u][v]+w);
}
}
printf("%lld\n",ans);
}
#10069. 「一本通 3.1 练习 4」Tree
代码
//如果我们把白色的边增大,
//那么这棵最小生成树里面的白色边就会少一些
//(因为边越大,排序的次序就越靠后),反之亦然。
//那么我们跑一个二分,把所有白边减一个mid,
//如果在此时最小生成树的白边数量比题目要求大,
//就说明目前mid的值大了,将其减少,否则将其增大。
//刚好题目也给了,边权值的范围:0 ~100
#include "stdio.h"
#include "algorithm"
using namespace std;
#include "string.h"
int ans;
int d,b,need;
struct ppp
{
int u,v,w,co;
bool operator<(const ppp &z)const
{
if(w!=z.w)
return w<z.w;
return co<z.co;
}
}p[100009];
int a[50009];
int find(int x)
{
if(a[x]==-1) a[x]=x;
if(a[x]==x) return x;
return a[x]=find(a[x]);
}
int ppp(int m)
{
for(int i=0;i<b;i++)//改变白边的值
{
if(p[i].co==0)
p[i].w+=m;
}
sort(p,p+b);
memset(a,-1,sizeof(a));
int s=0;
int k=0;//记录白边数
ans=0;//最先生成树权值
for(int i=0;i<b;i++)
{
if(s>=d-1)
break;
int n1=find(p[i].u);
int n2=find(p[i].v);
if(n1!=n2)
{
a[n1]=n2;
s++;
if(p[i].co==0)
k++;
ans+=p[i].w;
}
}
for(int i=0;i<b;i++)//把白边变回原来的值
{
if(p[i].co==0)
p[i].w-=m;
}
return k;
}
int main()
{
scanf("%d%d%d",&d,&b,&need);
for(int i=0;i<b;i++)
scanf("%d%d%d%d",&p[i].u,&p[i].v,&p[i].w,&p[i].co);
int r=0;
int k=-100;
int j=100;
int mi,s;
while(k<=j)//二分
{
mi=(int)(k+j)/2;
s=ppp(mi);
if(s>=need)
k=mi+1,r=mi;
else
j=mi-1;
// if(s>=need)为什么>和=可以写在一起了,在二分中>在像=靠齐,他的边界为=
// 在二分中而<也在向=靠齐,他的边界也为=,可不可以把=和<放在一起
//注意:如果这样写就不对
// if(s>need)
// k=mi+1;
// else
// j=mi-1,r=mi;
//应为他要的的是最小权,当s==need时,我们要判断是否有更小的mi,满足s==need;
//正确的是mi在减小,判断是否有更小的mi,满足s==need;
//错误的是mi在增加 ,判断是否有更大的mi,满足s==need;
}
s=ppp(r);
printf("%d\n",ans-need*r);//最后别忘了白边值变回来
}