洛谷Oj-P1315 觀光公交-貪心

問題描述:
風景迷人的小城Y 市,擁有n 個美麗的景點。由於慕名而來的遊客越來越多,Y 市特意安排了一輛觀光公交車,爲遊客提供更便捷的交通服務。觀光公交車在第 0 分鐘出現在 1號景點,隨後依次前往 2、3 、4 ……n 號景點。從第 i 號景點開到第 i+1 號景點需要 Di 分鐘。任意時刻,公交車只能往前開,或在景點處等待。
設共有m 個遊客,每位遊客需要乘車1 次從一個景點到達另一個景點,第i 位遊客在Ti 分鐘來到景點 Ai ,希望乘車前往景點Bi (Ai

struct P
{
    int t;//到達出發車站的時間
    int a;//出發地
    int b;//目標地
    int arr;//到達目標車站的時間
};
P p[10010];
int d[1010];//從i到i+1所花費的時間
int sum_peo[1010];//乘車區間[i,i+1]的乘客數
int arr_time[1010];//從景點i去往別處景點的最晚到達車站的人到達的時間
bool cmp(const P &a,const P &b)//按出發地從小到大排序,出發地相同就按到達出發地的時間從小到大排序
{
    if(a.a == b.a)
        return a.t < b.t;
    return a.a < b.a;
}
struct Ans
{
    int cnt_peo;//車上的人數
    int cnt_k;//最多使用的加速器的個數
};
vector<Ans> v;
bool compare(const Ans &a,const Ans &b)//比較函數之二
{
    return a.cnt_peo > b.cnt_peo;//按人數從大到小排序
}
int main()
{
    int n,m,k;
    cin >> n >> m >> k;//輸入景點數、乘客數和加速器數
    for(int i = 1; i <= n - 1; ++i)//輸入耗時
        cin >> d[i];
    for(int i = 1; i <= m; ++i)//輸入乘客信息
        cin >> p[i].t >> p[i].a >> p[i].b;
    sort(p + 1,p + m + 1,cmp);//按照出發點由小到大排序
    for(int i = 1; i <= m; ++i)//對於每一位乘客
        arr_time[p[i].a] = p[i].t;//迭代
    for(int i = 1; i <= m; ++i)//對於每一位乘客
        for(int j = p[i].a; j <= p[i].b - 1; ++j)
            sum_peo[j]++;
    //以下部分就是一個模擬
    int cur_time = 0;
    for(int i = 1; i <= n - 1; ++i)//對於每一個可以作爲出發點的景點(不使用加速器)
    {
        //乘客齊了就發車,乘客不齊就等待幾分鐘
        cur_time += max(0,arr_time[i] - cur_time);
        //發車嘍
        cur_time += d[i];//加上觀光車路上花費的時間(這是觀光車到達下一站的時間)
        for(int j = 1; j <= m; ++j)//爲到達目的地的乘客賦上到達的時間
            if(p[j].b == i + 1)//注意是下一站
                p[j].arr = cur_time;
//      核心代碼
//      乘車區間[i,i+1]中乘客的人數
//      到達下一站的時候人齊了,就有必要加速
//      到達下一站的時候人還不齊,就沒有必要加速
//      就算加了速還是要等,對答案沒有貢獻
        if(i == n - 1)
            v.push_back({sum_peo[i],d[i]});
        else
            v.push_back({sum_peo[i],max(0,cur_time - arr_time[i + 1])});
    }
    //經過對拍(就拍了一組數據),計算不用加速器所花費時間的代碼沒有問題
    //以下代碼忽視了對之後上車的人的影響的分類討論
    int cost_time = 0;
    for(int i = 1; i <= m; ++i)
        cost_time += p[i].arr - p[i].t;
    sort(v.begin(),v.end(),compare);
    for(int i = 0; i <= v.size() - 1; ++i)
    {
        if(k >= v[i].cnt_k)
        {
            k -= v[i].cnt_k;
            cost_time -= v[i].cnt_k * v[i].cnt_peo;
        }
        else
        {
            cost_time -= k * v[i].cnt_peo;
            break;
        }
    }
    cout << cost_time << endl;
    return 0;
}

AC代碼:

int n,m,k,ans;
int bus[1010];//bus[i]爲觀光車到達第i個景點的時間(絕對量)
int d[1010];//d[i]爲從第i個景點到第i+1個景點路上所花的時間
int arr_time[1010];//arr_time[i]爲最晚到第i個景點的乘客到達的時間
int get_off[1010];//get_off[i]爲在第i個景點下車的人數
int sum[1010];
void init()
{
    cin >> n >> m >> k;//景點數、乘客數、加速器數
    for(int i = 1; i <= n - 1; ++i)
        cin >> d[i];//路程時間
    for(int i = 1; i <= m; ++i)
    {
        int t,a,b;
        cin >> t >> a >> b;
        ans -= t;//由於所花時間的總和等於到達的時間減去來到的時間再求和,這裏先把來到的時間減去,之後再加上到達的時間
        arr_time[a] = max(arr_time[a],t);
        get_off[b]++;
    }
    return;
}
void solve()
{
    while(k--)//對於每一個加速器。不斷貪心,不斷迭代。因爲前一次貪可能會影響下一次貪心
    {
        for(int i = 1; i <= n; ++i)//公式
            //觀光車到達第i個頂點的時間等於從第i-1個景點發車的時間加上路上所花的時間
            bus[i] = max(bus[i - 1],arr_time[i - 1]) + d[i - 1];
        for(int i = n; i >= 2; --i)//倒着推受益乘客的人數
        {
            if(d[i-1] == 0)//如果加速器使得從第i-1個景點到第i個景點的時間變爲了0,則[i - 1,i]的受益乘客爲0
                sum[i - 1] = 0;
            else//若不爲0
            {
                sum[i - 1] = get_off[i];//[i - 1,i]受益乘客的人數就等於在第i個景點下車的人數
                if(bus[i] > arr_time[i])//如果觀光車到達第i個景點的時候人已經到齊了
                    sum[i - 1] += sum[i];//[i - 1,i]受益的乘客的人數要加上[i,i + 1]的,否則,就卡住了
            }
        }
        int max_sum = 0;是0而不是-inf
        int j;
        for(int i = 1; i <= n - 1; ++i)//找出受益乘客最多的那一段
            if(sum[i] > max_sum)//起碼要大於0
            {
                max_sum = sum[i];
                j = i;
            }
        if(max_sum == 0)//如果無法繼續使用加速器
            break;//跳出循環
        else
            d[j]--;//否則就減去路程
    }
    for(int i = 1; i <= n; ++i)
            bus[i]= max(bus[i - 1],arr_time[i - 1]) + d[i-1];//再遞推一遍
    for(int i = 1; i <= n; ++i)
        ans += bus[i] * get_off[i];//加上到達第i個景點的時間與在第i個景點下車的人數
    return;
}
int main()
{
    init();
    solve();
    cout << ans << endl;
    return 0;
}

心路歷程:
經過分析可以知道加速器最好在車上乘客比較多的時候使用,這樣對於減少旅行時間總和的貢獻比較大。但是早早地到達下一站但是乘客還沒到齊也是不能出發,白白地浪費時間和加速器,所以使用加速器也要有個度。綜合以上兩種因素,既要考慮此時此刻乘客的人數,又要考慮不能加速過頭,如何分配加速器還是有點難度啊
有以下的因素需要考慮
①從第i個景點到第i+1個景點的觀光車上有幾位乘客
②最晚到達第i個景點的乘客將於第幾分鐘到達

先把總的時間加起來,再減去加速器的貢獻

觀光車有早到、準點到和晚到這三種情況。如果早到了的話,就只能在那裏等着了。準點到不在話下。如果晚到的話,就要使用加速器使得觀光車準點到。可是此時是否有使用加速器的必要呢?我們說還要考慮車上的人數。不妨這樣:把此時車上的人數和使用的加速器個數上限(多用無益)給出,利用貪心的思想排序即可
需要注意的是,如果某車站沒有乘客上車,則arr_time爲0。

如果我是司機,當前的時間爲cur。等乘客到齊後(①已經到齊cur>=arr_time,立即發車②還沒到齊cur < arr_time,需要在原地進行等待arr_time - cur分鐘)從第i個景點出發–>cur加上在路上花費的時間得出到達下一站的時間(下車的乘客請帶好隨身物品準備下車)。繼續重複以上過程。
以上是我的分析,可惜除了模擬當司機的那塊,其它的都有問題
答案=求和(到達目的地時間-到達出發點時間)
我們發現到達出發點時間是不可更改的,所以儘量讓更多的人早早地到達目的地是貪心的關鍵
假如我們對[i,i+1]這條路線上使用了加速器,在[i,i+1]中的乘客都會受到影響,即早到第i+1個景點。那對之後的乘客會不會有影響呢,以下分兩種情況,車到了第i+1個景點時最後一個人還沒有到。那麼從第i+1個景點出發且已經到達的人依然要等待,這一部分不能早到。車到了第i+1個景點時人已經到齊了,那麼拉上人就走,所有人都能早到。這個分析是比以上我的分析更加深入的,不錯。
到達該景點的時間等於到達該景點前一個景點的時間加上到達該景點的路的時間,或者是到達該景點的前一個景點的最晚上車的人的時間加上到達該景點的路的時間
!a與a == 0是等價的
真的是佩服寫出這個程序的人

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章