sdu数据结构可是的石油网络题
运用剪枝的算法,方法原创。
剪枝思路有4个
1.完全没必要放的点。(出度为0的点)
2.必须要放的点。(父节点到其的最短边加上该店到其子节点的最大边的距离大于p_max也就是最大压力值的点),一般这个就可以确定百分之八十的节点了。
3.如果一个放置方案不成立,那么这个放置方案的子集也不成立,不需要在检查这些情况。(比如一个放了k个节点的方案不成立,那么可以直接推理出有2^k-1种其他方案不成立,需要额外的空间记录,相当于空间换时间)。
4.放5个的情况成立,那么再产生放五个,六个七个的方案就直接枪毙,不用看。(最好想,也是性能提升最大的剪枝)
本来还想做二分,但是感觉比较麻烦。
代码如下,用vs编译好像会出错,我用的c## odeblocks
#include<vector>
#include<iostream>
#include<set>
#include<string.h>
#include<queue>
#include<math.h>
#define MY_MAX 1000 //此值不能过小
using namespace std;
int final_ans=MY_MAX;//先给予一个极大值
bool record[1000];
struct booster_map
{
public:
int booster_num;
bool* bst_map;
booster_map(int n){
bst_map=new bool[n+1];
}
booster_map(){
}
};
void updatefailure(int x,int cur_num,int cur_weight)
{
;
}
class my_graph
{
public:
int p_max;
int num_v;
int num_e;
int source=1;
bool* place_map;
int** edges;
int** longest;
int** short_dis;//存储floyd结果
bool* must_none;//必须不能放置的节点,不考虑源点1
bool* must_place;//必须放置的节点位置,同样不考虑源点1
int least_place;
int least_dont_place;
int* indegree;//
int* outdegree;
int * pre; //当前各个节点的pre值
int min_ac;//当前最小的放置节点个数,如果生成数比这个大可以放弃
my_graph(int n,int m,int p){
min_ac=num_v-1;//如果除源点外全部放置,一定能够成立
num_v=n;
num_e=m;
p_max=p;
least_place=least_dont_place=0;
place_map=new bool[100];
indegree=new int[n+1];
outdegree=new int[n+1];
must_none=new bool[n+1];
must_place=new bool[n+1];
memset(place_map,false,100);
edges=new int* [n+1];
longest=new int*[n+1];
short_dis=new int* [n+1];
pre=new int [n+1];
for(int i=1;i<=n;i++){
must_none[i]=false;
must_place[i]=false;//初始状态认为都有可能放置或不放置
indegree[i]=outdegree[i]=0;
edges[i]=new int[n+1];
longest[i]=new int[n+1];
short_dis[i]=new int[n+1];
pre[i]=-1;//-1表示未到达,0表示刚好压力能到达
}
pre[1]=p_max;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
edges[i][j]=MY_MAX;//初始化为无穷
short_dis[i][j]=MY_MAX;
longest[i][j]=-1;//-1初始化表示不存在
}
}
}
vector<booster_map> all_maps;
set<int> new_activited;
int generate_map(int x);//生成穷举列表
void pre_set();
void assess();
void floyd();
void test_floyd();
void first_pre_generate_from_source();
void genrate_from_activited();//根据new_activated的元素更新pre值,假设有上一轮被激活的,并将这一轮新激活的节点更新
bool check_all_points();//检查是否所有点都已经到达
bool check_longest();
void print_pre();
void print_longest();
void cal_empty_node();//计算必须放置为空的位置
void cal_essential();//记录必须放置放大器的节点
bool check_cur_map();//检查当前放置序列是否满足要求(一定不能放的位置和一定放的位置的放置情况
void generate_all_maps();//将生成的全部符合要求的穷举列表存入vector中
bool erase_unnecessary_edge();//删去不必要的最长边
};
void my_graph::test_floyd()
{
for(int i=1;i<=num_v;i++){
for(int j=1;j<=num_v;j++){
cout<<edges[i][j]<<" ";
}
cout<<"\n";
}
cout<<"\n";
for(int i=1;i<=num_v;i++){
for(int j=1;j<=num_v;j++){
cout<<short_dis[i][j]<<" ";
}
cout<<"\n";
}
}
void my_graph::pre_set()
{
for(int i=1;i<=num_v;i++){
pre[i]=-1;
}
pre[1]=p_max;
new_activited.clear();
}
void my_graph::floyd()
{
for(int i=1;i<=num_v;i++){
short_dis[i][i]=0;//每个点到自身距离为0
}
for(int k=1;k<=num_v;k++)
for(int i=1;i<=num_v;i++){
for(int j=1;j<=num_v;j++){
if(short_dis[i][j]>short_dis[i][k]+short_dis[k][j]){
short_dis[i][j]=short_dis[i][k]+short_dis[k][j];
}
}
}
}
int my_graph::generate_map(int x)//生成穷举列表,返回放置放大器的个数
{
// vector<int> debug;
int res=0;
for(int i=2;i<=num_v;i++){//第一个点一定是true 源点
place_map[i]=x%2;
if(place_map[i]==true){
// debug.push_back(i);
res++;
}
x=x/2;
}
/*
cout<<"放置放大器为:";
for(int i=0;i<debug.size();i++){
cout<<debug[i]<<" ";
}
cout<<"\n";
*/
return res;
}
void my_graph::first_pre_generate_from_source()//从源点开始更新pre,且将激活的booster存入activited
{
// vector<int> debug;
int * row=short_dis[1];//第一行的弗洛伊德结果
for(int i=2;i<=num_v;i++){//source so begin from 2
if(row[i]<=p_max){
pre[i]=p_max-row[i];//假如距离小于等于压力,更新pre值
if(place_map[i]==true){//假如此处放置booster,则激活
new_activited.insert(i);
// debug.push_back(i);
}
}
}
/*
cout<<"从源点1开始激活的放大器为:";
for(int i=0;i<debug.size();i++){
cout<<debug[i]<<" ";
}
cout<<"\n";
*/
}
void my_graph::genrate_from_activited()//根据new_activated的元素更新pre值,假设有上一轮被激活的,并将这一轮新激活的节点更新
{
bool have_entered[100];
memset(have_entered,false,100);
queue<int> q;
for(auto it=new_activited.begin();it!=new_activited.end();it++){//把被源点激活的函数放入队列
int cur=*it;
q.push(cur);
have_entered[cur]=true;//表示是否进入队列
}
while(q.empty()==false)
{
int cur_s=q.front();
q.pop();
int* row=short_dis[cur_s];
for(int i=1;i<=num_v;i++){
if(i==cur_s){
pre[i]=p_max;
continue;
}
if(row[i]<=p_max){
pre[i]=max(pre[i],p_max-row[i]);//注意,此时其他源点的造成的pre值可能比当前值大
if(place_map[i]==true&&have_entered[i]==false){//假如二进制生成图允许,且这一放大器未被使用过,加入队列
q.push(i);
have_entered[i]=true;
}
}
}
}
//cout<<"endprocess\n";
}
bool my_graph::check_all_points()//检查是否所有点都已经到达
{
for(int i=2;i<=num_v;i++){
if(pre[i]==-1){
return false;
}
}
return true;
}
bool my_graph::check_longest()
{
for(int i=1;i<=num_v;i++){
for(int j=1;j<=num_v;j++){
if(i==j){
continue;
}
if(longest[i][j]==-1){
continue;
}
if(pre[i]<longest[i][j]){//此时源点信号强度小于最长边长度
return false;
}
}
}
return true;
}
void my_graph::print_pre()
{
for(int i=1;i<=num_v;i++){
cout<<"node"<<i<<" "<<pre[i]<<" ";
}
cout<<"\n";
}
void my_graph::print_longest()
{
for(int i=1;i<=num_v;i++){
for(int j=1;j<=num_v;j++){
cout<<longest[i][j]<<" ";
}
cout<<"\n";
}
}
void my_graph::cal_empty_node()//计算必须放置为空的位置
{
//vector<int> debug;
for(int i=2;i<=num_v;i++){
if(outdegree[i]==0){
must_none[i]=true;
least_dont_place++;
// debug.push_back(i);
}
}
/*
cout<<"完全没必要放置的节点为:";
for(int i=0;i<debug.size();i++){
cout<<debug[i]<<" ";
}
cout<<"\n";
*/
}
bool my_graph::erase_unnecessary_edge()//删去不必要的最长边
{
int* l_row=longest[1];//以源点为起点的最长边
for(int i=2;i<=num_v;i++){
if(l_row[i]==-1){
continue;//不存在对应边
}
if(l_row[i]<=p_max){
l_row[i]=-1;
}
}
}
void my_graph::cal_essential()//记录必须放置放大器的节点
{
//vector<int> debug;
for(int i=2;i<=num_v;i++){
if(must_none[i]==true){
continue;//已经计算过这里肯定不用放
}
int min_in_edge=MY_MAX;//记录最短入边
for(int j=1;j<=num_v;j++){
min_in_edge=min(min_in_edge,edges[j][i]);//至少有一个点指向当前节点,因此min_in_edge不为MY_MAX
}
int max_out_edge=0;
for(int j=2;j<=num_v;j++){ //肯定不会有指向1的边
max_out_edge=max(max_out_edge,longest[i][j]);
}
if(p_max<min_in_edge+max_out_edge){//此位置必须放置放大器
must_place[i]=true;
// debug.push_back(i);
least_place++;
}
}
/*
cout<<"必须放置的节点为:";
for(int i=0;i<debug.size();i++){
cout<<debug[i]<<" ";
}
cout<<"\n";
*/
}
bool my_graph::check_cur_map()//检查当前放置序列是否满足要求(一定不能放的位置和一定放的位置的放置情况
{
for(int i=2;i<=num_v;i++){
if(place_map[i]==true){
if(must_none[i]==true){
return false;
}
}
if(place_map[i]==false){
if(must_place[i]==true){
return false;
}
}
}
return true;
}
void my_graph::generate_all_maps()//将生成的全部符合要求的穷举列表存入vector中, 等会再写
{
int max_num=pow(2,num_v-1)-1;
for(int i=0;i<=max_num;i++){
int cur=i;
booster_map* new_map=new booster_map();
for(int j=2;j<=num_v;j++){
new_map->bst_map[j]=cur%2;
if(new_map->bst_map[j]==true){
new_map->booster_num++;
}
}
if(new_map->booster_num>num_v-least_dont_place-1||new_map->booster_num<least_place){
delete new_map;//
}
else{
all_maps.push_back(*new_map);
}
}
}
bool* failure;//失败者图
void update_failure(int x,int cur_num,int cur_weight)
{
cout<<"stuck"<<x;
if(x==0){
failure[cur_num]=true;
return;
}
if(x%2==0){
x=x/2;
update_failure(x,cur_num,2*cur_weight);
return ;
}
else{
x=x/2;
update_failure(x,cur_num+cur_weight,2*cur_weight);//当前位置取1
update_failure(x,cur_num,2*cur_weight);//当前位置取0
return;
}
return;
}
void record_map(my_graph& g)//将放大器图位置保存
{
for(int i=2;i<=g.num_v;i++){
record[i]=g.place_map[i];
}
}
int show_pre[100];
void record_pre(my_graph& g){
for(int i=2;i<=g.num_v;i++){
show_pre[i]=g.pre[i];
}
}
int main()
{
int n,m,l;
// ios::sync_with_stdio(false);
、、 freopen("input1.in","r",stdin);
cin>>n>>m>>l;
my_graph graph(n,m,l);
int x,y,c;
for(int i=0;i<m;i++){
cin>>x>>y>>c;
graph.indegree[y]++;
graph.outdegree[x]++;
graph.longest[x][y]=max(graph.longest[x][y],c);//记录最长边
if(graph.edges[x][y]>c){
graph.edges[x][y]=c;
graph.short_dis[x][y]=c;
}
}
graph.floyd();
graph.cal_empty_node();
graph.cal_essential();
int lo_bound=graph.least_place;//二分下界
int hi_bound=n-graph.least_dont_place-1;//二分上界
int max_genrate=pow(2,n-1)-1;
int generate_num=max_genrate;
failure=new bool[1000000];//!!!!数组开小了会溢出
memset(failure,0,1000000);
memset(record,0,1000);
// cout<<"gen"<<generate_num;
int mid_begin=max_genrate/2;
for(;generate_num>=0;generate_num--){
int temp=graph.generate_map(generate_num);//生成当前图放置
if(temp>=final_ans){//当前地图放置数量比最目前最小数量大,直接跳过
continue;
}
if(graph.check_cur_map()==false){//当前放置位置一定不对(有多放或少放的
continue;
}
if(failure[generate_num]==true){//当前放置为之前失败放置的子集
continue;
}
graph.pre_set(); //初始化
graph.first_pre_generate_from_source();//从源点激活
graph.genrate_from_activited(); //依次激活其他点
if(graph.check_all_points()==true&&graph.check_longest()==true){
// cout<<"this plan is accessble\n";
// graph.print_pre();
// graph.print_longest();
final_ans=min(final_ans,temp);
record_map(graph);
record_pre(graph);
}else{
update_failure(generate_num,0,1);
}
// cout<<"\n";
}
cout<<"num of booster is "<<final_ans<<"\n";
// cout<<final_ans;
cout<<"放大器位置为:";
for(int i=2;i<=n;i++){
if(record[i]==true){
cout<<"node"<<i<<" ";
}
}
cout<<"\n";
show_pre[1]=l;
cout<<"各节点压力值为:\n";
for(int i=1;i<=n;i++){
cout<<"node"<<i<<":"<<show_pre[i]<<"\n";
}
return 0;
}