题目链接
题目大意:从n个点选m个点构成一颗生成树,要求这个生成树的比率最小。
分析:看到数据范围就很明显了,n个点选m个点,dfs暴力枚举或者状压挨个枚举,然后更新答案就可以了。
下面是状压
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<vector>
#define MAIN main
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int N=1e5+10;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x>0?1:-1;
}
int g[21][21],vis[21],w[21],dis[21],done[21];
double r;
vector<int>ans;
int s;
void prim(int n,int m)
{
int sum=0;
for(int i=1;i<=n;i++){
if(!vis[i]) continue;
sum+=w[i];
dis[i]=g[s][i];
done[i]=0;
}
done[s]=1;
int mst=0;
for(int i=1;i<m;i++){
int min_edge=inf;
int num;
for(int i=1;i<=n;i++){
if(!vis[i]||done[i]) continue;
if(min_edge>dis[i]){
min_edge=dis[i];
num=i;
}
}
done[num]=1;
mst+=min_edge;
for(int i=1;i<=n;i++){
if(done[i]||!vis[i]) continue;
dis[i]=min(dis[i],g[num][i]);
}
}
double new_ans=(mst*1.0)/(sum*1.0);
if(sgn(r-new_ans)>0){
r=new_ans;
ans.clear();
for(int i=1;i<=n;i++){
if(!vis[i]) continue;
ans.push_back(i);
}
}
}
bool update(int x,int m)
{
memset(vis,0,sizeof(vis));
int now=1,num=0;
while(x)
{
if(x&1) vis[now]=1,num++,s=now;
x>>=1;
now++;
}
return num==m;
}
int MAIN()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0) break;
r=1e10;
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&g[i][j]);
}
}
for(int j=0;j<=(1<<n)-1;j++){
if(update(j,m)) prim(n,m);
}
printf("%d",ans[0]);
for(int i=1;i<(int)ans.size();i++){
printf(" %d",ans[i]);
}
printf("\n");
}
return 0;
}
下面是dfs
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define MAIN main
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int N=1e5+10;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x>0?1:-1;
}
int g[21][21],vis[21],w[21],dis[21],done[21];
double r;
vector<int>ans;
int s;
void prim(int n,int m)//选定第一个作为初始结点
{
int sum=0;
for(int i=1;i<=n;i++){
if(!vis[i]) continue;
sum+=w[i];
dis[i]=g[s][i];
done[i]=0;
}
done[s]=1;
int mst=0;
for(int i=1;i<m;i++){
int min_edge=inf;
int num;
for(int i=1;i<=n;i++){
if(!vis[i]||done[i]) continue;
if(min_edge>dis[i]){
min_edge=dis[i];
num=i;
}
}
done[num]=1;
mst+=min_edge;
for(int i=1;i<=n;i++){
if(done[i]||!vis[i]) continue;
dis[i]=min(dis[i],g[num][i]);
}
}
double new_ans=(mst*1.0)/(sum*1.0);
if(sgn(r-new_ans)>0){
r=new_ans;
ans.clear();
for(int i=1;i<=n;i++){
if(!vis[i]) continue;
ans.push_back(i);
}
}
}
void dfs(int n,int m,int num,int st)
{
if(num==m){
prim(n,m);
return;
}
for(int i=st;i<=n-m+num+1;i++)
{
if(vis[i]) continue;
vis[i]=1;
if(num==0) s=i;
dfs(n,m,num+1,i+1);
vis[i]=0;
}
}
int MAIN()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
if(n==0&&m==0) break;
r=1e10;
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&g[i][j]);
}
}
memset(vis,0,sizeof(vis));
dfs(n,m,0,1);
sort(ans.begin(),ans.end());
printf("%d",ans[0]);
for(int i=1;i<(int)ans.size();i++){
printf(" %d",ans[i]);
}
printf("\n");
}
return 0;
}