0
已解决
赵逸凡
初级启示者
初级启示者
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int s,n,fa[510],m,cnt;
struct point{
double x,y;
bool del;
}dot[510];
struct edge{
int u,v;
double w;
bool flag;
}a[25000];
int size;
double dis(int st,int ed)
{
return sqrt((dot[st].x-dot[ed].x)*(dot[st].x-dot[ed].x)+(dot[st].y-dot[ed].y)*(dot[st].y-dot[ed].y));
}
void init()
{
for(int i=1;i<=n;i++)
fa[i]=i;
}
int find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return ;
fa[x]=y;
}
bool same(int x,int y)
{
if(find(x)==find(y))
return true;
return false;
}
bool cmp(edge l,edge r)
{
return l.w<r.w;
}
bool cmp2(edge l,edge r)
{
if(l.flag!=r.flag)
return l.flag>r.flag;
return l.w>r.w;
}
int Kruskal()
{
double MST=0;
init();
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(!same(a[i].u,a[i].v))
{
unite(a[i].u,a[i].v);
a[i].flag=true;
MST+=a[i].w;
}
}
return MST;
}
int main()
{
cin>>s>>n;
for(int i=1;i<=n;i++)
cin>>dot[i].x>>dot[i].y;
m=n*(n-1)/2;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
a[++size]=(edge){i,j,dis(i,j)};
double mst=Kruskal();
sort(a+1,a+m+1,cmp2);
for(int i=1;i<n;i++)
{
edge e=a[i];
if(cnt==s)
break;
if(!dot[e.u].del&&!dot[e.v].del)
{
dot[e.u].del=true;
dot[e.v].del=true;
mst-=e.w;
cnt++;
}
if(!dot[e.u].del&&dot[e.v].del)
{
dot[e.u].del=true;
mst-=e.w;
cnt++;
}
if(dot[e.u].del&&!dot[e.v].del)
{
dot[e.v].del=true;
mst-=e.w;
cnt++;
}
}
printf("%.2f",mst);
return 0;
}
我的思路是用Kruskal求出MST后,再开始贪心:每次在n-1条边(边数是MST的一条定理)中取出边权最大的边,将其两端点置为s座城市的两个结点,然后一直取边、标记点,直到点数到达s时停止,但样例输出的是199.87,求助哪里错了?(老师没回)
0
0
赵逸凡
初级启示者
初级启示者
我一开始以为是我的贪心策略可能出现当取k个边时,恰好总共标记的还剩一个点就能有s个点,我的程序此时会依旧选择边权最大的,而理论上应该取有一个点已被标记过的最大权的边
0
赵逸凡
初级启示者
初级启示者
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
int s,n,fa[510],m,cnt;
struct point{
double x,y;
bool del;
}dot[510];
struct edge{
int u,v;
double w;
bool flag;
}a[25000];
int size;
double dis(int st,int ed)
{
return sqrt((dot[st].x-dot[ed].x)*(dot[st].x-dot[ed].x)+(dot[st].y-dot[ed].y)*(dot[st].y-dot[ed].y));
}
void init()
{
for(int i=1;i<=n;i++)
fa[i]=i;
}
int find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return ;
fa[x]=y;
}
bool same(int x,int y)
{
if(find(x)==find(y))
return true;
return false;
}
bool cmp(edge l,edge r)
{
return l.w<r.w;
}
bool cmp2(edge l,edge r)
{
if(l.flag!=r.flag)
return l.flag>r.flag;
return l.w>r.w;
}
int Kruskal()
{
double MST=0;
init();
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(!same(a[i].u,a[i].v))
{
unite(a[i].u,a[i].v);
a[i].flag=true;
MST+=a[i].w;
}
}
return MST;
}
int main()
{
cin>>s>>n;
for(int i=1;i<=n;i++)
cin>>dot[i].x>>dot[i].y;
m=n*(n-1)/2;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
a[++size]=(edge){i,j,dis(i,j)};
double mst=Kruskal();
sort(a+1,a+m+1,cmp2);
for(int i=1;i<n;i++)
{
edge e=a[i];
if(cnt==s)
break;
if(!dot[e.u].del&&!dot[e.v].del)
{
dot[e.u].del=true;
dot[e.v].del=true;
if(cnt+2<=s)
{
mst-=e.w;
cnt+=2;
}
}
if(cnt==s)
break;
if(!dot[e.u].del&&dot[e.v].del)
{
dot[e.u].del=true;
mst-=e.w;
cnt++;
}
if(cnt==s)
break;
if(dot[e.u].del&&!dot[e.v].del)
{
dot[e.v].del=true;
mst-=e.w;
cnt++;
}
if(cnt==s)
break;
}
printf("%.2f",mst);
return 0;
}
对于两个端点均没标记过的最小生成树的最大权边进行了最后情况(即cnt=s-1)的优化,然而输出的数大于样例,我在cnt==s的情况多次进行了判断,然而还是没用处。正解貌似是筛出n-s条边,但O(n!/s!/(n-s)!)的复杂度我懒得写了。
0
赵逸凡
初级启示者
初级启示者
@沈峻宇 将n-s条边进行组合,还要用到dfs的性质来枚举各种可能性,我还是听老师讲吧
赵逸凡在2020-09-26 14:22:48追加了内容
update:
样例过了,但是WA
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int s,n,fa[25000],m,cnt;
struct point{
double x,y;
bool del;
}dot[25000];
struct edge{
int u,v;
double w;
bool flag;
}a[100000];
int size;
double dis(int st,int ed)
{
return sqrt((dot[st].x-dot[ed].x)*(dot[st].x-dot[ed].x)+(dot[st].y-dot[ed].y)*(dot[st].y-dot[ed].y));
}
void init()
{
for(int i=1;i<=n;i++)
fa[i]=i;
}
int find(int x)
{
if(fa[x]==x)
return x;
return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return ;
fa[x]=y;
}
bool same(int x,int y)
{
if(find(x)==find(y))
return true;
return false;
}
bool cmp(edge l,edge r)
{
return l.w<r.w;
}
bool cmp2(edge l,edge r)
{
if(l.flag!=r.flag)
return l.flag>r.flag;
return l.w>r.w;
}
int Kruskal()
{
double MST=0;
init();
sort(a+1,a+m+1,cmp);
for(int i=1;i<=m;i++)
{
if(!same(a[i].u,a[i].v))
{
unite(a[i].u,a[i].v);
a[i].flag=true;
MST+=a[i].w;
}
}
return MST;
}
int main()
{
cin>>s>>n;
for(int i=1;i<=n;i++)
cin>>dot[i].x>>dot[i].y;
m=n*(n-1)/2;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
a[++size]=(edge){i,j,dis(i,j)};
double mst=Kruskal();
double maxi=-100;
sort(a+1,a+m+1,cmp2);
for(int i=1;i<n;i++)
{
edge e=a[i];
if(cnt==s)
break;
if(!dot[e.u].del&&!dot[e.v].del)
{
dot[e.u].del=true;
dot[e.v].del=true;
if(cnt+2<=s)
{
mst-=e.w;
cnt+=2;
}
}
if(cnt==s)
break;
if(!dot[e.u].del&&dot[e.v].del)
{
dot[e.u].del=true;
mst-=e.w;
cnt++;
}
if(cnt==s)
break;
if(dot[e.u].del&&!dot[e.v].del)
{
dot[e.v].del=true;
mst-=e.w;
cnt++;
}
if(cnt==s)
break;
}
for(int i=1;i<n;i++)
{
edge e=a[i];
if(!dot[e.v].del||!dot[e.u].del)
maxi=max(maxi,e.w);
}
printf("%.2f",maxi);
return 0;
}
我标记了最大权值边,但还是错了,请问为什么
@黄子扬
0
0