您的位置:首页 > 其它

HDU 2489 Minimal Ratio Tree(dfs枚举+最小生成树)

2013-09-01 16:26 447 查看
想到枚举m个点,然后求最小生成树,ratio即为最小生成树的边权/总的点权。
但是怎么枚举这m个点,实在不会。
网上查了一下大牛们的解法,用dfs枚举,没想到dfs还有这么个作用。

参考链接:/article/2664019.html

#include <stdio.h>
#include <string.h>
#include <set>
#include <vector>
#include <queue>

using namespace std;

const int INF=0x3f3f3f3f;
int n,m,tot; //tot为选取的m个点的总权值
int choseNode[16];  //存储选取的m个点
int tmp[16];  //存储最小ratio的m个点
int nodew[16],w[16][16];  //nodew[i]存储点i的权值,w[i][j]存储边的权值
int dis[16],vis[16];
double minratio=0x3f3f3f3f;

int prim(){
int ans=0;
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
int t,idx;
dis[choseNode[0]]=0;
for(int i=1;i<=m;i++){
t=INF;
for(int i=0;i<m;i++){
if(!vis[choseNode[i]] && dis[choseNode[i]]<t){
t=dis[choseNode[i]];
idx=choseNode[i];
}
}
vis[idx]=1;
ans+=t;
for(int i=0;i<m;i++){
int u=choseNode[i];
if(!vis[u] && w[idx][u]<dis[u]){
dis[u]=w[idx][u];
}
}
}
return ans;
}
/*
dfs枚举m个点,num代表目前选取了多少个点,k代表第num个点为k。
表示前num个点选自1~k,剩余的点从k+1~n中选。
*/
void dfs(int k,int num){
if(num==m){
tot=0;
for(int i=0;i<m;i++){
tot+=nodew[choseNode[i]];
}
int sum=prim();
double tmpratio=sum*1.0/tot;
if(tmpratio<minratio){
minratio=tmpratio;
for(int i=0;i<m;i++){
tmp[i]=choseNode[i];
}
}
return;   //忘记写return了。。。
}
//若剩余的点的个数(n-k)加上目前选取的个数num小于m的话,说明即使接下来n-k个点都选取,也选不足m个点,直接return
if(n-k+num<m)
return;
for(int i=k+1;i<=n;i++){
//选的点用数组存起来
choseNode[num]=i;
dfs(i,num+1);
}
}
int main()
{
int a;
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0 && m==0)
break;
memset(w,0,sizeof(w));
minratio=INF*0.1;
for(int i=1;i<=n;i++){
scanf("%d",&nodew[i]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++)
scanf("%d",&a);
for(int j=i+1;j<=n;j++){
scanf("%d",&a);
w[i][j]=w[j][i]=a;
}
}
for(int i=1;i<=n;i++){
choseNode[0]=i;
dfs(i,1);
}
for(int i=0;i<m-1;i++){
printf("%d ",tmp[i]);
}
printf("%d",tmp[m-1]);
printf("\n");
}
return 0;
}


最后再附上网上看到的另一种dfs枚举的写法:

//调用时:dfs(1,0,0);

//dep表示点的编号,cnt表示选取的点的个数,sum_pw表示目前选取了cnt个点后总的点权值
void dfs(int dep, int cnt, int sum_pw) {
if(cnt == m) {
...;
return ;
}
if(dep == n + 1) return ;
use[dep] = true;  //选取点dep,这里use[i]=true表示选取点i,在用prim求最小生成树的时候有用
dfs(dep + 1, cnt + 1, sum_pw + weight[dep]);
use[dep] = false; //不选取点dep
dfs(dep + 1, cnt, sum_pw);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: