POJ 1180 Batch Scheduling

$dp$,斜率优化。

有一种很直观的$dp$方式:

设$dp[j][i]$表示前$i$个数字,分成了$j$组的最小代价。$dp$转移方程很容易写,斜率优化也很容易。斜率优化之后时间复杂度为$O(n^2)$,最坏情况大约是是$5000$万的复杂度。我尝试了一下,但是不幸超时了。但是当我特判掉$s=0$的情况(该情况肯定是分成$n$组)之后,去尝试分$1000$组,$500$组,$250$组......能否$AC$,最终发现后台数据除去$s=0$的情况之后,最坏的情况是分成$166$组就可以得到最优解。因此这样$500ms$水过去了。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<ctime>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-10;
void File()
{
    freopen("D:\in.txt","r",stdin);
    freopen("D:\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar();
    x = 0;
    while(!isdigit(c)) c = getchar();
    while(isdigit(c))
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
}

int dp[2][10010];
int n,s;
int q[10010],f1,f2;
int sumT[10010],sumF[10010];
int flag;
int ans;

bool delete1(int t,int a,int b,int c)
{
    if(
        dp[flag^1][b]-(t+1)*s*sumF[b]-sumT[c]*sumF[b]<=
        dp[flag^1][a]-(t+1)*s*sumF[a]-sumT[c]*sumF[a]
    ) return 1;
    return 0;
}

bool delete2(int t,int a,int b,int c)
{
    if(
        ((dp[flag^1][c]-(t+1)*s*sumF[c])-(dp[flag^1][b]-(t+1)*s*sumF[b]))*(sumF[b]-sumF[a])<=
        ((dp[flag^1][b]-(t+1)*s*sumF[b])-(dp[flag^1][a]-(t+1)*s*sumF[a]))*(sumF[c]-sumF[b])
    ) return 1;
    return 0;
}

int main()
{
    scanf("%d%d",&n,&s);

    for(int i=1; i<=n; i++)
    {
        int tt,ff;
        scanf("%d%d",&tt,&ff);
        sumT[i]=sumT[i-1]+tt;
        sumF[i]=sumF[i-1]+ff;
    }

    if(s==0)
    {
        ans=0;
        for(int i=1; i<=n; i++) ans=ans+sumT[i]*(sumF[i]-sumF[i-1]);
        printf("%d
",ans);
        return 0;
    }

    flag=0;
    ans=0x7FFFFFFF;

    for(int i=1; i<=n; i++) dp[flag][i]=(sumT[i]+s)*sumF[i];
    ans=min(ans,dp[flag][n]);

    for(int j=2; j<=min(166,n); j++)
    {
        flag=flag^1;
        f1=f2=0;
        q[0]=j-1;
        for(int i=j; i<=n; i++)
        {
            while(1)
            {
                if(f2-f1+1<2) break;
                if(delete1(j-1,q[f1],q[f1+1],i)) f1++;
                else break;
            }

            dp[flag][i]=dp[flag^1][q[f1]]+(j*s+sumT[i])*(sumF[i]-sumF[q[f1]]);

            while(1)
            {
                if(f2-f1+1<2) break;
                if(delete2(j-1,q[f2-1],q[f2],i)) f2--;
                else break;
            }

            f2++;
            q[f2]=i;
        }
        ans=min(ans,dp[flag][n]);
    }

    printf("%d
",ans);
    
    return 0;
}
View Code

水过去的方法当然是不可取的,复杂度太高了。看了神犇博客,发现这题可以$O(n)$做。

设$dp[i]$表示以$i$为任务起点,到达终点的最小代价。

那么,$dp[i]=min(dp[x]+(sumF[n]-sumF[i-1])*(s+sumT[x-1]-sumT[i-1]))$。

进行斜率优化之后时间复杂度降到了$O(n)$。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<ctime>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-10;
void File()
{
    freopen("D:\in.txt","r",stdin);
    freopen("D:\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar();
    x = 0;
    while(!isdigit(c)) c = getchar();
    while(isdigit(c))
    {
        x = x * 10 + c - '0';
        c = getchar();
    }
}

int n,s;
int T[10010],F[10010],dp[10010];
int sumT[10010],sumF[10010];
int f1,f2,q[10010];

bool delete1(int a,int b,int c)
{
    int A=sumF[n]-sumF[c-1];
    if(dp[a]+A*sumT[a-1]>=dp[b]+A*sumT[b-1]) return 1;
    return 0;
}

bool delete2(int a,int b,int c)
{
    if((dp[b]-dp[a])*(sumT[c-1]-sumT[b-1])<=
       (dp[c]-dp[b])*(sumT[b-1]-sumT[a-1])) return 1;
    return 0;
}

int main()
{
    scanf("%d%d",&n,&s);

    for(int i=1; i<=n; i++)
    {
        scanf("%d%d",&T[i],&F[i]);
        sumT[i]=sumT[i-1]+T[i];
        sumF[i]=sumF[i-1]+F[i];
    }

    f1=f2=0; q[0]=n+1;
    for(int i=n;i>=1;i--)
    {
        while(1)
        {
            if(f2-f1+1<2) break;
            if(delete1(q[f1],q[f1+1],i)) f1++;
            else break;
        }

        dp[i]=dp[q[f1]]+(sumF[n]-sumF[i-1])*(s+sumT[q[f1]-1]-sumT[i-1]);

        while(1)
        {
            if(f2-f1+1<2) break;
            if(delete2(q[f2-1],q[f2],i)) f2--;
            else break;
        }

        f2++;
        q[f2]=i;
    }

    printf("%d
",dp[1]);

    return 0;
}
原文地址:https://www.cnblogs.com/zufezzt/p/6358802.html