POJ 1167 The Buses

5 篇文章 0 订阅
4 篇文章 0 订阅

Name: The Buses
P_ID: POJ 1167
题目链接:http://poj.org/problem?id=1167

题意关键点:
1. 同一时刻可能有不同路线的车到达。这个要区分。
2. 每种路线的interval一定大于等于这条路线的第一辆车到达的时间。(强剪枝,因为这说明29分钟之后到的车都不可能是某条路线的第一辆车。)

题目分析:
这个题目的难点在于两个:
1. 建模
2. 剪枝

首先是建模。
一个比较明显的解决办法是dfs搜索。但是怎么建立模型比较麻烦。
我们可以基于路线建模。建一个名叫busRoute的struct,包含三个元素,begin, interval, times(这条路线中包括的车次)。建立结构数组,route[i]表示第i条路线。建立数组sumBus[i]记录第i分钟到达的车的数量。有了这些,我们可以开始读入数据并存储。

然后是剪枝。
我们首先要在route中存下所有可能包含的路线,注意,由于所有可能路线的第一辆车到达时间一定小于等于29,所以循环控制小于等于29(剪枝1)。所有可能包含的路线存储下之后,有一个很重要的剪枝工作,就是对这些所有可能的路线,按照其所包含的times从大到小进行排序(剪枝2)。如果当前已经确定的路线数量,加上假设剩下的没用过的车次都用下一个路线车次大小的路线(不一定存在)表示所需要的路线数量,就已经大于当前ans值,那么说明,这条路是死路,以后的不用做了,无用功。(类似POJ 1190)(剪枝3)

参考代码:

/**
 *name: The Buses
 *date: 2016-04-21
 *note: dfs + modelling + pruning    difficult 
 */
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
using namespace std;

struct busRoute {
    int begin;
    int interval;
    int times;
};

busRoute route[1300];
int n, sumBus[1300];
int tot = 0, ans = 17;

bool comp(const busRoute a, const busRoute b)
{
    return (a.times > b.times);
}

bool checkRoute(int a, int b)
{
    for(int i=a; i<60; i+=b)
    {
        if(sumBus[i]==0)
            return false;
    }
    return true;
}

//num表示目前已经确定的路线数量
void dfs(int a, int num)
{
    if(n<=0)
    {
        if(num < ans)
            ans = num;
        return;
    }
    for(int k=a; k<tot; ++k)
    {
        if(num + n/(route[k].times) >= ans) return;//剪枝3

        if(checkRoute(route[k].begin, route[k].interval))
        {
            int temp = route[k].interval;
            for (int i = route[k].begin; i < 60; i += temp)//入栈
            {
                sumBus[i]--;
                n--;
            }
            dfs(k, num + 1);//深搜
            for (int i = route[k].begin; i < 60; i += temp)//回溯
            {
                sumBus[i]++;
                n++;
            }
        }
    }
}




int main()
{
    scanf("%d", &n);
    memset(sumBus, 0, sizeof(sumBus));
    int bus;
    for(int i=0; i<n; ++i)
    {
        scanf("%d", &bus);
        sumBus[bus]++;
    }

    tot = 0;
    for(int i=0; i<30; ++i)//剪枝1
    {
        if(sumBus[i]==0) continue;
        for(int j=i+1; j<60-i; ++j)
        {
            if(checkRoute(i, j))
            {
                route[tot].begin = i;
                route[tot].interval = j;
                route[tot].times = (59-i)/j + 1;
                tot++;
            }
        }
    }

    sort(route, route+tot, comp);//剪枝2
    ans = 17;
    dfs(0, 0);

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


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值