您的位置:首页 > 其它

Kingdoms UVA - 12507 状压||dfs搜索+最小生成树

2017-06-24 22:02 330 查看
A kingdom has n cities numbered 1 to n, and some bidirectional roads connecting cities. The capital is always city 1. After a war, all the roads of the kingdom are destroyed. The king wants to rebuild some of the roads to connect the cities, but unfortunately,
the kingdom is running out of money. The total cost of rebuilding roads should not exceed K. Given the list of m roads that can be rebuilt (other roads are severely damaged and cannot be rebuilt), the king decided to maximize the total population in the capital
and all other cities that are connected (directly or indirectly) with the capital (we call it “accessible population”), can you help him?

Input The first line of input contains a single integer T (T ≤ 20), the number of test cases. Each test case begins with three integers n (4 ≤ n ≤ 16), m (1 ≤ m ≤ 100) and K (1 ≤ K ≤ 100,000). The second line contains n positive integers pi (1 ≤ pi ≤ 10,000),
the population of each city. Each of the following m lines contains three positive integers u, v, c (1 ≤ u,v ≤ n, 1 ≤ c ≤ 1000), representing a destroyed road connecting city u and v, whose rebuilding cost is c. Note that two cities can be directly connected
by more than one road, but a road cannot directly connect a city and itself.

Output

For each test case, print the maximal accessible population.

Sample Input

2 4 6 6 500 400 300 200 1 2 4 1 3 3 1 4 2 4 3 5 2 4 6 3 2 7 4 6 5 500 400 300 200 1 2 4 1 3 3 1 4 2 4 3 5 2 4 6 3 2 7

Sample Output

1100 1000

做法一:暴力每一个节点的存在或者不存在,存在为1,不存在为0,用最小生成树连接一下,如果这个最小值小于k,并且可以生成最小生成树,那么就算出他的总人口求一个最大值

#include<bits/stdc++.h>//time:40ms
using namespace std;
int pep[20];
int pre[20];
int vis[20];
int maxans=0;
struct aa
{
int u,v,w;
} edge[111];
int findboss(int x)
{
if(x==pre[x])
return x;
else
{
pre[x]=findboss(pre[x]);
return pre[x];
}
}
bool comb(int a,int b)//并查集连接
{
int aa=findboss(a);
int bb=findboss(b);
if(aa==bb)
return 0;
else
{
pre[aa]=bb;
return 1;
}
}
bool cmp(const aa a,const aa b)
{
return a.w<b.w;
}
void dfs(int now,int n,int m,int k,int cnts)//遍历到的节点编号,n,m,k,当前开放的节点数
{
if(now>n)//叶节点的时候状态已经确定
{
for(int i=1; i<=now; i++)
pre[i]=i;
int cnt=0;
int sum=0;
for(int i=0; i<m; i++)
{
int u=edge[i].u;
int v=edge[i].v;
int w=edge[i].w;
if(vis[u]&&vis[v]&&comb(u,v))//u ,v 开放,u,v可以连接
{
cnt++;
sum+=w;
}
if(cnt==cnts-1)
{
break;
}
}
if((cnt==cnts-1)&&(sum<=k))
{
int tsum=0;
for(int i=1; i<=now; i++)//所有人口
{
if(vis[i])
tsum+=pep[i];
}

// printf("%d\n",tsum);
if(tsum>maxans)
maxans=tsum;
}
return;
}
vis[now+1]=0;
dfs(now+1,n,m,k,cnts);//下一个节点不开放
vis[now+1]=1;
dfs(now+1,n,m,k,cnts+1);//下一个节点开放

}
int main()
{
// freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k;
maxans=0;
memset(vis,0,sizeof(vis));
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; i++)
scanf("%d",&pep[i]);
for(int i=0; i<m; i++)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
sort(edge,edge+m,cmp);
vis[1]=1;
dfs(1,n,m,k,1);//从第一个节点开始搜索,开放节点数为1
printf("%d\n",maxans);

}
}

做法二:其实是做法一的一种改进,我们根本不用使用dfs去搜索,直接用二进制状态表示这一堆状态就可以了。然后对于每一种状态具体去讨论。用时相对较少。

下面给出代码

#include<bits/stdc++.h>
using namespace std;
int pep[20];
int pre[20];
int vis[20];
int dp[1<<16];
int maxans=0;
struct aa
{
int u,v,w;
} edge[111];
int findboss(int x)
{
if(x==pre[x])
return x;
else
{
pre[x]=findboss(pre[x]);
return pre[x];
}
}
bool comb(int a,int b)//并查集连接
{
int aa=findboss(a);
int bb=findboss(b);
if(aa==bb)
return 0;
else
{
pre[aa]=bb;
return 1;
}
}
bool cmp(const aa a,const aa b)
{
return a.w<b.w;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,k;
maxans=0;
memset(vis,0,sizeof(vis));
scanf("%d%d%d",&n,&m,&k);
for(int i=1; i<=n; i++)
scanf("%d",&pep[i]);
for(int i=0; i<m; i++)
scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
sort(edge,edge+m,cmp);
int len=1<<n;//状态压缩到(0-len-1)
for(int i=0; i<len; i++)
{
int cnts=0;
for(int j=0; j<n; j++)//搜索出该状态存在的点
{
if(((1<<j)&i)==0)
vis[j+1]=0;
else
{
vis[j+1]=1;
cnts++;
}
}
if(vis[1]!=1)//如果点不包含1节点
continue;
for(int j=1; j<=n; j++)//初始化并查集操作
pre[j]=j;
int cnt=0;
int sum=0;
for(int j=0; j<m; j++)//遍历所有可行点,进行最小生成树操作
{
int u=edge[j].u;
int v=edge[j].v;
int w=edge[j].w;
if(vis[u]&&vis[v]&&comb(u,v))//u ,v 开放,u,v可以连接
{
cnt++;
sum+=w;
}
if(cnt==cnts-1)
{
break;
}
}
if((cnt==cnts-1)&&(sum<=k))//钱数目符合,并且可以生成最小生成树
{
int tsum=0;
for(int j=1; j<=n; j++)//统计所有人口
{
if(vis[j])
tsum+=pep[j];
}
dp[i]=tsum;
if(dp[i]>maxans)//最大值求解
maxans=dp[i];
}

}

printf("%d\n",maxans);

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: