题意:在n个结点的完全图中,每条边和每个点都有一个权值。
定义:
从中选出m个结点组成一个连通图,顺序输出构成最小的ratio时所选取的m个点。
思路:边权之和最小,想到prim,同时n最大是15,直接暴力。先dfs出m个顶点,然后求出对应的ratio,找出最小的radio,输出结果就行了。
计算了一下15以内组合数的最大值是6000+,此时m为7或8,算上prim的O(n^2),时间是够的。一发AC!
#include<cstdio> #include<cstring> #include<iostream> #include<cstdlib> #include<cmath> #include<set> #include<algorithm> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const int N=18; int n,m,g[N][N],node[N]; int arr[N]; double ans; bool vis[N]; int Prim(){ //lowcost[i]记录结果集到结点i的最短距离,mst[i]记录结果集中到i最短距离的端点 int minn=inf,id=0,lowcost[N],mst[N]; int res=0; for(int i=0;i<n;++i){ if(vis[i]){ id=i; break; } } for(int i=0;i<n;++i){ if(vis[i]){ lowcost[i]=g[id][i]; mst[i]=id; } } for(int i=1;i<m;++i){ minn=inf; for(int i=0;i<n;++i){ if(vis[i]&&lowcost[i]&&lowcost[i]<minn){ minn=lowcost[i]; id=i; } } res+=minn; lowcost[id]=0; for(int i=0;i<n;++i){ if(vis[i]&&lowcost[i]&&g[id][i]<lowcost[i]){ lowcost[i]=g[id][i]; mst[i]=id; } } } return res; } void dfs(int k,int num){//k是当前下标,num是已标记点数 //printf("dfs:k=%d num=%d\n",k,num); if(num==m){ int we=Prim(); int wn=0; for(int i=0;i<n;++i){ if(vis[i]){ wn+=node[i]; } } double res=(double)we/wn; if(res<ans){ ans=res; for(int i=0,j=0;i<n;++i){ if(vis[i]){ arr[j++]=i;//数组arr记录选取的结点 } } } }else{ if(k==n)return;//搜索终点 if((n-k)<(m-num))return;//剪枝,减少递归层数 vis[k]=1; dfs(k+1,num+1); vis[k]=0; dfs(k+1,num); } } int main(){ while(~scanf("%d%d",&n,&m),m||n){ for(int i=0;i<n;++i){ scanf("%d",&node[i]); } for(int i=0;i<n;++i){ for(int j=0;j<n;++j){ scanf("%d",&g[i][j]); } } memset(vis,0,sizeof(vis)); ans=99999999.0; dfs(0,0); for(int i=0;i<m-1;++i){ printf("%d ",arr[i]+1);//题目下标从1开始 } printf("%d\n",arr[m-1]+1); } }