Code Jam--The Bored Traveling Salesman

来源:互联网 发布:nt数据看宝宝男女最准 编辑:程序博客网 时间:2024/05/16 15:47

原题:https://code.google.com/codejam/contest/2994486/dashboard#s=p2

官方题解:https://code.google.com/codejam/contest/2994486/dashboard#s=a&a=2


题意:给定一个规模为N的无向连通图,每个点有一个5位10进制数的邮政编码,当遍历这个图时我们把访问到的zip按先后顺序连接起来可以形成一个5*N位的数,问能行成的最小数。其中遍历时前向进入某个点的道路只能使用一次,返回某点的次数不受限制。

题解:注意到起始点必定是zip最小的点,因为在无向图中,一个点若能到其它任何点,则从此点出发肯定能按照题意即将每个边前向后向遍历一次且能遍历所有点。首先这个结论在两层的无向连通树里面是显然成立的,然后可以用归纳法证明我们通过遍历由zip最小的点到达所有点的DFS生成树,可以遍历所有点且前向边和其反向边只用一次(其实正是DFS及其返回的过程)。

  • 一开始只用到了起始点的贪心,再用可返回父节点的DFS搜所有可能的路径复杂度(N-1)!。看了题解才明白是可以一直用贪心思想的。我们将N个点动态分配为:minpath[],active[]。其中minPath保存已知最优答案的zip各点,active保存已知最优且还能返回再访问的点,因为在路径中可能返回则返回的点我们不会再访问。
  • **找到栈active所有点的邻接点中能到达剩下所有点且zip最小的点。
  1. 从active栈顶的点的邻接点开始,其中我们仍然使用最初的贪心思想,看它DFS能访问到多少个其他点,若还不足以到达所有点,则我们必须层层返回active中的点看它们是否能到达剩下的点。
  2. 当栈顶的点的所有邻接点已经验证完毕时,这时我们就要看倒数第二个进入active的点的邻接点,即可能的路径为从最后进入active的点返回倒数第二个点。
  3. 。。。
  • 若最优的点不是active栈顶的点的邻接点,则我们一直出栈直到最优的那个点的父节点,然后将最优点放入栈中,重复步骤1直到minPath的容量达到N。

#include<iostream>#include<cstdio>#include<cstring>#include<vector>using namespace std;#define INF 0X7F7F7F7Fclass solve{private:    int N;    int M;    int t;    int zip[51];    int minPath[51];    int active[51];    char InPath[51];    int activeNum;    int tmpActiveNum;    int pathLen;    int start;    char reachGraph[51][51];    vector<vector<int> > g;public:    solve(int i):t(i)    {        memset(reachGraph,0,sizeof(reachGraph));        processIn();        calcMinZip();        printf("Case #%d: ",t);        for(int i = 0;i < N;i++)        {            printf("%d",minPath[i]);        }        printf("\n");    }    int processIn();    int calcMinZip();    bool IsReachAll(int nodeNo);    int DFS(int nodeNo,int* reachNodeNum,char* vis);};int solve::DFS(int nodeNo,int* reachNodeNum,char* vis){    if(*reachNodeNum == N)        return 1;    int adjSize = g[nodeNo].size();    int nextNode;    for(int i = 0; i < adjSize;i++)    {        nextNode = g[nodeNo][i];        if(!InPath[nextNode]&&!vis[nextNode])        {            vis[nextNode] = 1;            (*reachNodeNum)++;            DFS(nextNode,reachNodeNum,vis);        }    }    return 1;}bool solve::IsReachAll(int nodeNo){    int reachNodeNum = pathLen+1;    char vis[51];    memset(vis,0,sizeof(vis));    for(int i = tmpActiveNum;i >= 0;i--)    {        if(i == tmpActiveNum)        {            DFS(nodeNo,&reachNodeNum,vis);     //直接从nodeNo能访问到的其他点        }        else        {            DFS(active[i],&reachNodeNum,vis);   //从nodeNo往回走到其它active点再访问到的其他点        }        if(reachNodeNum == N)            return 1;    }    return 0;}int solve::calcMinZip(){    int minZip;    int minZipIndex;    int bestActiveNum;    activeNum = 0;    active[activeNum++] = start;    memset(InPath,0,sizeof(InPath));    InPath[start] = 1;    pathLen = 0;    minPath[pathLen++] = zip[start];    while(pathLen < N)    {        tmpActiveNum = activeNum;        minZip = INF;        for(int i = activeNum-1;i >= 0;i--)            //active数组减小表示要先往回走,再到达我们要求的下一个点        {            tmpActiveNum = i+1;            for(int j = 1;j <= N;j++)            //遍历active数组中点的邻接点,找出zip最小且通过路径能到达所有其他点的点            {                if(InPath[j])                    continue;                if(reachGraph[active[i]][j])                {                    InPath[j] = 1;                    if(minZip > zip[j]&&IsReachAll(j))                    //zip比已知的小才进行搜索看能否到其他所有点                    {                        minZip = zip[j];                        minZipIndex = j;                        bestActiveNum = tmpActiveNum;                    }                    InPath[j] = 0;                }            }        }        activeNum = bestActiveNum;     //更新active数组大小,则有些点再也不会访问到        active[activeNum++] = minZipIndex;        minPath[pathLen++] = minZip;        InPath[minZipIndex] = 1;    }    return 1;}int solve::processIn(){    scanf("%d%d",&N,&M);    int a,b;    int minZip = INF;    g.resize(N+1);    for(int i = 1;i <= N;i++)    {        scanf("%d",zip+i);        if(minZip > zip[i])        {            minZip = zip[i];            start = i;        }    }    while(M--)    {        scanf("%d%d",&a,&b);        reachGraph[a][b] = reachGraph[b][a] = 1;        g[a].push_back(b);        g[b].push_back(a);    }    return 0;}int main(){    int T;    scanf("%d",&T);    for(int i = 1;i <= T;i++)    {        solve the_bored_traveling_salesman(i);    }    return 0;}


0 0
原创粉丝点击