cf题面优化
比胜过程中想了一个贪心——把全部城市按照自建代价排序,排在第一的城市确定自建,以后依次判断排在后面的城市要自建仍是要链接前面的。这么作WA13了(第一次忘开long long
WA4)。spa
赛后看看题解,又参考了以前一样WA13的 Artoriax的代码,大概发现了这种作法的漏洞。假设自建代价是\(c_1<c_2<c_3\),能够构造连边的代价,使得在花费最小的链接方式中,连边应该是1—3—2,我以前那样的作法,1号城市自建之后,判断2号城市要自建仍是要连1号城市,再判断3号城市要自建仍是要连1号城市或者2号城市。.net
具体的hack数据以下——code
3 1 1 2 2 2 1 1 5 100 1 2 1
说简单点大概就是,一、二、3自建代价是一、五、100,1到2连边代价是5,1到3的连边代价是2,2到3的连边代价是3。最小代价答案是6,我那种方法跑出来是8。排序
我后来AC的思路大概是:首先假设每一个点都自建,那么每一个点的代价就是自建代价。而后按照代价排序,用代价最小的点去更新后面那些点,若是能更新用电代价,就把那些点链接到当前点。而后进入下一轮循环,排除上一次代价最小的点,把剩下的点再次按照代价排序,而后用这些点中代价最小的去更新其余的,以此类推。ip
官方题解还提供了一种更通常的想法:这题其实就是求一个最小生成树,图是这么建的——首先全部点之间连边,边权就是链接代价,而后加一个0号点,全部点向0号点连边,边权是自建代价。这么一想,我AC的思路就是毫无堆优化的、还不如线性直接找最小值的、很蠢的Prim了。get
#include<cstdio> #include<algorithm> int n; struct City{ int id; long long x,y; long long cc,kk; bool self; int fa; bool operator < (const City & a)const{ return cc<a.cc; } }c[2005]; int main() { // freopen("test.in","r",stdin); scanf("%d",&n); for(int i=1;i<=n;i++) { c[i].id=i; c[i].self=1; scanf("%lld%lld",&c[i].x,&c[i].y); } for(int i=1;i<=n;i++) scanf("%lld",&c[i].cc); for(int i=1;i<=n;i++) scanf("%lld",&c[i].kk); long long ans=0,selfnum=0; for(int i=1;i<=n;i++) { std::sort(c+i,c+1+n);//大概就是要随时排序,每次找到最小的 ans+=c[i].cc; if(c[i].self) selfnum++; for(int j=i+1;j<=n;j++) { long long cost=(c[i].kk+c[j].kk)*(std::abs(c[i].x-c[j].x)+std::abs(c[i].y-c[j].y)); if(cost<c[j].cc) { c[j].cc=cost; c[j].self=0;//放弃自建 c[j].fa=c[i].id; } } } printf("%lld\n%lld\n",ans,selfnum); for(int i=1;i<=n;i++) if(c[i].self) printf("%d ",c[i].id); printf("\n%lld\n",n-selfnum); for(int i=1;i<=n;i++) if(!c[i].self) printf("%d %d\n",c[i].id,c[i].fa); return 0; }