2011 FinalProblem F—MachineWork

引例题目分析

本题主要是考察了经典DP中的机器问题,先看一个简单的例子
总公司拥有高效设备M台,准备分给下属的N个分公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M≤15,N≤10。分配原则:每个公司有权获得任意数目的设备,但总台数不超过设备数M。

输入格式
第一行有两个数,第一个数是分公司数N,第二个数是设备台数M。

接下来是一个N*M的矩阵,表明了第 I个公司分配 J台机器的盈利。

输出格式
第1行为最大盈利值

第2到第n为第i分公司分x台

输入输出样例
输入
3 3
30 40 50
20 30 50
20 25 30

输出
70
1 1
2 1
3 1

解题思路;
很明显,这题划分的阶段应该是“工厂数”,状态应该是“机器数”,那么很自然就会想到用 f( i , j ) 来表示有 i 个工厂,分配 j 台机器时最大价值。我们最终要求的就是f( N , M)。很显然,假设我们想求 i 个工厂 j 台机器的最大总价值,其一定等于前 i-1个工厂分配k个机器的最大总价值 + 第 i 个工厂获得 j-k 时的价值的最大值,其中 0 <= k <= j 。

咱们来描述一下推导过程:
假设:
f(N,M)是N个工厂分配M台机器所产生的最大利润
那么 f ( i - 1 , k )表示什么呢?
是不是可以描述为(i - 1)个工厂分配k台机器所产生的最大利润

对于f(i,j)来说 f ( i - 1 , k )是不是前(i - 1)个工厂已经分配到了机器,只有第i个还没分配到
但是当前机器还剩下(j-k)台,使用wight(i,j-k)来描述第i个工厂分配(j-k)台机器产生的利润

所以
i个工厂,j台机器产生的最大利益为:
f ( i , j ) = max { f ( i - 1 , k ) + wight ( i , j - k ) } (0 <= k <= j)
讲到这里基本上已经理解了怎么推导出简单的dp方程

代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
//定义NxM 为20x20
const int maxn = 20;
int val[maxn][maxn];
int f[maxn][maxn];
//dp函数公式
void dfs(int mx,int i,int j){
	if(i == 0) return;
	for(int k = 0;k <= j;k++){
		if(f[i-1][k] + val[i][j-k] == mx){
			dfs(f[i-1][k],i-1,k);
			printf("%d %d\n",i,j-k);
			break;
		}
	}
}
//遍历NxM 带入dp函数公式
void solve(int n,int m){
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			for(int k = 0;k <= j;k++){
				if(f[i][j] < f[i-1][j-k] + val[i][k]){
					f[i][j] = f[i-1][j-k]+val[i][k];
				}
			}
		}
	}
	printf("%d\n",f[n][m]);
	dfs(f[n][m],n,m); 
}
//输入N M ,调用solve()
int main(){
	int n,m;
	//freopen("123.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			scanf("%d",&val[i][j]);
		}
	}
	solve(n,m);
	return 0;
}

2011 FinalProblem F—MachineWork

题目:
你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先进的机械设备生产先进的机器。原来的那一台生产机器已经坏了,所以你要去为公司买一台新的生产机器。你的任务是在转型期内尽可能得到更大的收益。
在这段时间内,你要买卖机器,并且当机器被ACM公司拥有的时候,操控这些机器以获取利润。因为空间的限制,ACM公司在任何时候都只能最多拥有一台机器。
在转型期内,有若干台可能卖出的机器。对于每台机器Mi,你已经知道了其价格Pi和可以买入的日期Di。注意,如果不在第Di天买入机器Mi,那么别的人也会买走这一台机器,也就是说,以后你将没有机会购买这台机器了。如果ACM的钱低于一台机器的价格,那么你显然不可能买到这一台机器。
如果你在第Di天买入了机器Mi,那么ACM公司可以从第(Di)+1天开始使用这一台机器。每使用这台机器一天,就可以为公司创造出Gi美元的收益。
你可以决定要在买入之后的某一天,以一定的折扣价卖出这一台机器。收购市场对于每一台机器,都有一个折扣价Ri。你不能在卖出的那一天使用机器,但是你可以在卖出的那一天再买入一台新的。
在转型期结束后,ACM公司会卖掉当前所拥有的机器。你的任务就是最大化转型期间ACM公司可以得到的加粗样式收入。

数据范围
1<=Di<=D,1<=Ri< Pi<=109,1<=Gi<=109.

输入
输入包含若干组测试用例。
每一组测试用例的第一行有3个正整数N,C和D。
N是将会卖出的机器的台数(N<=10^5),
C是在转型期开始时公司拥有的美元数量(C<=10^9),
D是转型期持续的天数(D<=10^9)。
之后的N行每一行描述了一台机器的情况。
每一行有4个正整数Di,Pi,Ri和Gi,
Di表示这台机器卖出的时间
Pi购买这台机器需要的美元数量
R i卖出这台机器的折扣价
Gi使用这台机器可以得到的利润。
数据范围满足1<=Di<=D,1<=Ri<Pi<=10^9, 且1<=Gi<=10^9.
最后一组测试用例后面的一行由3个0组成,表示输入数据。
6 10 20
6 12 1 3
1 9 1 2
3 2 1 2
8 20 5 4
4 11 7 4
2 10 9 1
0 0 0

输出
对于每一组测试用例,输出测试用例的编号,之后给出ACM公司在第D+1天结束后可以得到的最大数量的美元。
请依照下面给出的样例输出。
Case 1: 44

题目分析

先假设F(i)表示买入第i台机器后的最大收益

F(i)= max{ F(j) +( D(i) - D(j) - 1 )*G(j) +R(j) } +P(i),其中(j<i)

F(i) = F(j) + D(i)*G(j) - D(j)*G(j) - G(j) + R(j) + P(i)

移项

F(j) - D(j)*G(j) - G(j) + R(j)= F(i) -P(i) - D(i)*G(j)

那么F(i)-P(i)可以看成是经过点(G(j),F(j)−D(j)∗G(j)−G(j)+R(j))的斜率为−D(i)的直线的最大截距.

我们对下标进行CDQ分治,将[l,mid]得到的点维护成一个斜率单减的凸壳,[mid+1,r]中的直线按照斜率递减的顺序依次进行匹配,计算答案即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100033
#define eps 1e-9
#define LL long long
using namespace std;
const double inf=1e19;
int n,C,D,top,st[N];
LL f[N];
struct data{
    LL d,r,p,g,id;
    double k;
}a[N],np[N];
struct point{
    LL x,y; bool pd;
    bool operator <(const point &b) const {
      return x<b.x||(x==b.x&&y>b.y);
    } 
}p[N],np1[N];
int cmp(data a,data b)
{
    return a.d<b.d;
}
double getk(int i,int j)
{
    if (p[i].x-p[j].x==0) return -inf;
    return (double)(p[i].y-p[j].y)*1.0/(double)(p[i].x-p[j].x)*1.0;
}
void divide(int l,int r)
{
    if (l==r) {
        f[l]=max(C-a[l].p,f[l]); p[l].pd=0;
        if (f[l]<0) f[l]=-1,p[l].pd=1;
        p[l].x=a[l].g;
        p[l].y=f[l]-a[l].d*a[l].g-a[l].g+a[l].r;
        return;
    }
    int mid=(l+r)/2;
    int l1=l; int l2=mid+1;
    for (int i=l;i<=r;i++)
     if (a[i].id<=mid) np[l1++]=a[i];
     else np[l2++]=a[i];
    for (int i=l;i<=r;i++) a[i]=np[i];
    divide(l,mid);
    int top=0;
    for (int i=l;i<=mid;i++) {
        if (p[i].pd) continue;
        while (top>=2&&getk(i,st[top])+eps>getk(st[top],st[top-1])) top--;
        st[++top]=i;
    }
    if (top) {
        int j=1;
        for (int i=mid+1;i<=r;i++) {
            while (j<top&&getk(st[j],st[j+1])>-a[i].d) 
             j++;
            int k=st[j];
            f[a[i].id]=max(f[a[i].id],p[k].y+p[k].x*a[i].d-a[i].p);
        }
    }
    divide(mid+1,r);
    l1=l; l2=mid+1;
    for (int i=l;i<=r;i++)
     if ((p[l1]<p[l2]||l2>r)&&l1<=mid) np1[i]=p[l1++];
     else np1[i]=p[l2++];
    for (int i=l;i<=r;i++)
      p[i]=np1[i];
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    int T=0;
    while (true) {
        scanf("%d%d%d",&n,&C,&D); T++;
        if (n==0&&C==0&&D==0) break;
        for (int i=1;i<=n;i++) scanf("%I64d%I64d%I64d%I64d",&a[i].d,&a[i].p,&a[i].r,&a[i].g);
        ++n; a[n].d=D+1; a[n].p=0; a[n].r=0; a[n].g=0;
        for (int i=1;i<=n;i++) f[i]=-1;
        sort(a+1,a+n+1,cmp);
        for (int i=1;i<=n;i++) a[i].id=i;
        divide(1,n);
        printf("Case %d: %lld\n",T,f[n]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值