1. 程式人生 > >序列劃分(正)

序列劃分(正)

題目

總工程師 題目描述 有M個軟體工程師正在完成一個大型軟體,需要開發若干個元件。 已知完成每個元件的開發時間儲存在一個數組A裡面,每個工程師只能獨立完成連續的元件, 並且所有的工程師都是並行工作, 請輸出完成這個軟體需要的最少時間

輸入說明 程式從當前路徑下的data.txt檔案中讀取測試資料。 第一行會有1個數值,表示M 第二行則是A陣列,元素之間以空格隔開

輸出說明 向標準輸出列印完成軟體需要的最少時間

示例 輸入:

2 3 1 4 表示有兩個工程師來完成3 個元件。 3個元件耗時分別是3/1/4.

輸出:

4 最好的分配方式為第一個工程師完成元件 1,2, 所以需要時間為4; 第二個工程師完成3, 所需要時間為4, 因為並行工作, 所以最小時間是4.

思路

  1. 注意每個工程師只能獨立完成連續的元件,故該題實際就是對序列進行劃分,設劃分M個序列後的最大子序列和為value,則題目需要找到一個最小的value。
  2. 而且時間肯定是正的,那麼不需要考慮負數序列。
  3. 那麼可以用貪心去解決了:假設正確答案是V,我們就假設一個值value去逼近V,直到用value可以劃分M個序列,而value-1不能劃分M個序列,此時value值即為V。

程式碼

#include <iostream>
#include <fstream>

using namespace std;

int m, n, a[1024];

bool judge(int
value) { int sum = 0, cnt = 0; for (int i = 0; i<n; i++) { // 若預分配超過value if (sum + a[i] > value) { sum = a[i]; cnt++; // 劃分超過m if (cnt == m) return false; } else { sum += a[i]; } } return
true; } int main() { int sum = 0; fstream file("data.txt"); file >> m; while (!file.eof()) { file >> a[n]; sum += a[n++]; } while (sum) { if (judge(sum) && !judge(sum - 1)) break; sum--; } cout << sum << endl; system("pause"); return 0; }
//問題分析:
//能否使m個連續子序列所有的s(i)均不超過x,則該命題成立的最小的x即為答案。該命題不難判斷,只需貪心,每次儘量從左
//向右儘量多劃分元素即可。
//我們把該問題轉化為遞迴分治問題,類似於二分查詢。首先取Sum和元素最大值的中值x,如果命題為假,那麼答案比x大;
//如果命題為真,則答案小於等於x。問題得解,複雜度為O(n*logSum)
#include<stdio.h>

int num[1005], n, m,Max=0,Left=0,Right=0;
/* 判斷劃分所子序列其和是否都不大於x  是-1,否-0*/
int Judge(int SumMin) 
{
    //每次往右劃分,劃分完後,所用的劃分線不大於m-1個即可 
    int count = 0, LeftSum = 0;//count已劃分子序列個數(其實count的值為劃分斜槓個數),LeftSum即將劃分的序列從右開始累和
    for (int i = 0; i<n; i++)
    {
        if (num[i]>SumMin)   
            return 0;//如果序列中有元素的值大於SumMin,則無論怎麼劃分都不成立
        if (LeftSum + num[i]>SumMin)  
        {
            count++;  
            LeftSum = num[i];
            if (count>m - 1)   
                return 0;//已劃分的子序列個數超過m
        }
        else
            LeftSum += num[i];   
    }
    return 1;
}

int BinarySelect()
{
    while (Left<Right)
    {
        int mod = Left + (Right - Left) / 2;
        if (Judge(mod)) 
            Right = mod;
        else 
            Left = mod + 1;
    }
    return Left;
}

int main()
{
    scanf("%d %d", &n, &m);
    for (int i = 0; i < n; i++)
    {
        scanf("%d", &num[i]);
        if (num[i] > Left)
            Left = num[i];
        Right += num[i];
    }
    printf("%d\n",BinarySelect());

    return 0;
}