遗传算法解一道笔试题

来源:互联网 发布:linux如何创建文件夹 编辑:程序博客网 时间:2024/05/19 14:56

题目大意如下:

有N个房间,通过N-1个门相连(每个房间的门数在1到3之间),现在有M个路由器,需要把M个路由器放置在其中一些房间中,使得放置路由和其周围的房间能收到wifi信号(假如一个房间放置了路由器,只有它自己以及与它相邻的房间可以收到它的信号)。每个房间都有一个满意度,如果 该房间可以收到WiFi信号则,总的满意度就会累加上该房间的满意度。现在问,怎样放置M个路由器使得总满意度最大。


输入:

第一行两个整数N    M,代表N个房间M个路由器,(2<=N<=1000, 1<=M<=N,    M<=100)

第二行N个数,代表每个房间满意度

接下来N-1行,表示N-1个门,其中每行两个整数,代表门联通的房间号


输出:

最大满意度


样例输入:

5 11 2 3 4 52 13 24 25 37 11 2 3 4 5 6 72 13 24 25 36 27 47 21 2 3 4 5 6 72 13 24 25 36 27 48 21 2 3 4 5 6 7 82 13 24 25 36 27 48 5

样例输出:

162329


分析:首先将题目抽象成N个节点的树,房间为节点,门为边,节点的权值为满意度,接下来从中选择M节点,让它覆盖的权最大。

从N个点中选M个点,有C(N,M)种,可以从两个方面考虑

1.考虑树中的非叶子结点num,分两种情况:

如果num<=M,直接选取非叶子结点放置,如果有剩余则再随便放在叶节点,这样能满足所有房间全覆盖;满意度为所有房间之和。

如果num>M,这种情况比较复杂,我也没分析明白


2.直接用遗传算法解决,每个房间有一个编号,从中选择M个房间。对于基因的编码有两种考虑,这两种方法导致的复杂度相差很大。

(1)染色体长度为M,每一个基因分别代表选中的房间号,整个编码长度较短,交叉复杂度较低。例如:N=9,M=5时 染色体可能为:2 6 3 7 1

(2)染色体长度为N,每一个基因代表一个房间,如果选中则相应位置为1,否则为0。例如:N=9,M=5染色体可能为011100110


下面的遗传算法是采用第一种编码方法:

(之前也写过遗传算法,这次写完好像纠正了一些理解上的偏差)


#include <iostream>#include <vector>#include <algorithm>#include <cmath>#include <ctime>#include <fstream>#include <iterator>#include <cstring>#define MAX 0x37777777#define LEN 1002using namespace std;//遗传参数double PC=0.8 ;     //交叉概率double PM=0.2 ;     //变异概率const int H=9   ;      //种群规模int G=1000  ;   //迭代次数int N;int M;double S[LEN];//graph parameterint cnt=0;struct Edge{    int v,next;}edge[2*LEN];int head[LEN];struct Answer{    double fit;    bool gen[LEN];    int chNodes[LEN];};vector<Answer> Popu;void addEdge(int u,int v)//邻接表(头插法){    edge[cnt].v=v;    edge[cnt].next=head[u];head[u]=cnt++;}bool Cmp(const Answer & a1,const Answer & a2){    return a1.fit>a2.fit;}void initInput(){    memset(head,-1,sizeof(head));    cin>>N>>M;    for(int i=0;i<N;i++)    {        cin>>S[i];    }    for(int i=0;i<N-1;i++)    {        int u,v;        cin>>u>>v;        u--,v--;//下标从0开始        addEdge(u,v);        addEdge(v,u);    }    //cout<<"input"<<endl;}void fitness(Answer &newGen){    int coverNodes[LEN]={0};    newGen.fit=0;    for(int j=0;j<M;j++)    {        int node=newGen.chNodes[j];        coverNodes[node]=1;        for(int e=head[node];e!=-1;e=edge[e].next)        {            int v=edge[e].v;            if(coverNodes[v]!=1)            {                coverNodes[v]=1;            }        }    }    for(int j=0;j<N;j++)    {        if(coverNodes[j]==1)        {            newGen.fit+=S[j];        }    }}void initPopu(){    for(int i=0;i<2*H;i++)    {        Answer newGen;        newGen.fit=0;        memset(newGen.gen,0,sizeof(newGen.gen));        memset(newGen.chNodes,0,sizeof(newGen.chNodes));        for(int j=0,k=0;j<M;)        {            int rom=rand()%N;            if(newGen.gen[rom]!=1)            {                newGen.gen[rom]=1;                newGen.chNodes[k]=rom;                k++;                j++;            }        }        fitness(newGen);        Popu.push_back(newGen);    }    //cout<<"initPopu"<<endl;}void choosePopu(){    vector<Answer> newPopu;    double sumFits=0;    double fits=0;    for(int j=0;j<Popu.size();j++)    {        sumFits+=(1.0/Popu[j].fit);    }    for(int i=0;i<H-1;i++)    {        double random=(rand()%998)/997.0;        fits=0;        int num=0;        for(int j=0;j<Popu.size();j++)        {            fits+=(1.0/Popu[j].fit/sumFits);            if(fits>=random)            {                num=j;//选中num                break;            }        }        newPopu.push_back(Popu[num]);    }    sort(Popu.begin(),Popu.end(),Cmp);    newPopu.push_back(Popu[0]);//精英选择,插在最后    swap(Popu,newPopu);}void cross1(int first,int second)//单点交叉{    Answer n1=Popu[first],n2=Popu[second];    int r1=rand()%M;    int r2=rand()%M;    if(r1==r2)return;    else if(r1>r2)    {        r1=r1^r2;        r2=r1^r2;        r1=r1^r2;    }    int parent1[LEN];    int parent2[LEN];    memset(parent1,-1,sizeof(parent1));    memset(parent2,-1,sizeof(parent2));    for(int j=r1;j<r2;j++)    {        parent1[j]=n1.chNodes[j];        parent2[j]=n2.chNodes[j];    }    for(int j=r1;j<r2;j++)    {        if(n1.gen[parent2[j]]==0)        {            n1.gen[parent2[j]]=1;            n1.gen[parent1[j]]=0;            n1.chNodes[j]=parent2[j];        }    }    for(int j=r1;j<r2;j++)    {        if(n2.gen[parent1[j]]==0)        {            n2.gen[parent1[j]]=1;            n2.gen[parent2[j]]=0;            n2.chNodes[j]=parent1[j];        }    }    fitness(n1);    Popu.push_back(n1);    fitness(n2);    Popu.push_back(n2);}void xCross(){    for(int i=0;i<H;i++)    {        double random=(rand()%998)/997.0;        if(random<PC)        {            int first=i;            int second=(i+(rand()%(H-1)))%(H);            //交叉            cross1(first,second);        }    }}void verify()//变异{    for(int i=H;i<Popu.size();i++)    {        double random=(rand()%998)/997.0;        if(random<PM)        {            Answer nv=Popu[i];            int del=rand()%M;            int delNode=nv.chNodes[del];            int add=rand()%N;            while(nv.gen[add]!=0)            {                add=rand()%N;            }            nv.gen[delNode]=0;            nv.gen[add]=1;            nv.chNodes[del]=add;            fitness(nv);            Popu.push_back(nv);        }    }}void GA(){    initPopu();//初始化种群    int k=0;//第k代数    for(;k<G;k++)    {        //选择(精英选择1+轮盘选择)        choosePopu();        //交叉        xCross();        //变异        verify();        //cout<<k<<":"<<(Popu[0]).fit<<endl;//第k次迭代最好代价    }    sort(Popu.begin(),Popu.end(),Cmp);    //Save(Popu[0]);    //copy((Popu[0]).path.begin(),(Popu[0]).path.end(),ostream_iterator<int>(cout," "));cout<<endl;//输出answer    cout<<(Popu[0]).fit<<endl;}int main(){    srand ( unsigned (time(NULL) ) );    initInput();    GA();    return 0;}




0 0