1. 程式人生 > 其它 >2022.3.18

2022.3.18

藍書

AcWing 186. 巴士

一開始想的是在搜尋的時候再列舉巴士的路線,但是發現搜尋的時候處理太麻煩了,應該在讀入的時候先預處理出一共可能有的路線,就是列舉每個起點和它的時間間隔,列舉起點只需要從0-30就可以了並不用像題解一樣列舉到60,因為是個等差數列,如果你公差都大於30了列舉的下一項肯定已大於60,,必定不合法。如果終點有某個點是沒有巴士停的話說明這條線路是不合法的,在列舉完了之後,因為題目已說明最多不會超過17條線路,所以可以迭代加深一層層的搜尋,當迭代到限定層數是確定一下當前已經算到的巴士數量是否和給的n相同。在每一層我們依次列舉每一條可能的線路,這裡我只想到了檢查一下列舉的起點和時間間隔是否合法,但是沒有想到題解的剪枝,結果超時。題解的剪枝是說噹噹前的列舉的路線的巴士數量乘以之後還沒有列舉層數加上當前的巴士數量還是小於n的話就需要回溯。如果合法就繼續搜尋下一層,同時需要將這條路線上經過的時間點的巴士數量依次-1,代表這條路線已經被用過了,最後還原現場的時候記得要加回去。一開始從1-n迴圈寫習慣了,結果題目是從0-59,後面調著調著才發現,下次要注意一點。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=300+10,INF=1e8;
int a[N],n,cnt,dep;
struct node
{
    int st, d, tot;
} bus[N];
bool cmp(node a,node b)
{
    return a.tot > b.tot;
}
bool check(int st, int d)
{
    while (st < 60)
    {
        if(a[st]==0)
            return 0;
        st += d;
    }
    return 1;
}
bool dfs(int x,int dep,int st,int sum)
{
    if(x==dep)
    {
        return sum == n;
    }

    for (int i = st; i <= cnt;i++)
    {
        int sta = bus[i].st, d = bus[i].d;
        if(bus[i].tot*(dep-x)+sum<n)
            continue;
        if(!check(sta,d))
            continue;
        for (int j = sta; j < 60;j+=d)
            a[j]--;
        if(dfs(x+1,dep,i,sum+bus[i].tot))
            return 1;
        for (int j = sta; j < 60;j+=d)
            a[j]++;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n;i++)
    {
        int x;
        cin >> x;
        a[x]++;
    }
    for (int i = 0; i < 30;i++)
    {
        for (int j = i + 1; i + j < 60;j++)
        {
            if(check(i,j))
            {
                bus[++cnt] = {i, j, (59 - i) / j + 1};
            }
        }
    }
    sort(bus + 1, bus + 1 + cnt, cmp);
    while(dep<=17)
    {
        if(dfs(0,dep,1,0))
            break;
        dep++;
    }
    cout << dep;
    return 0;
}