• POJ1821 Fence


    题目大意

    长度为n的墙,k个粉刷匠。第 i 个粉刷匠在 s[i] 块木板前,他最多可以刷包含 s[i] 的长度为 l[i] 的区间,他刷单位长度获得钱 p[i] 。求k个粉刷匠最多能赚多少钱?

    递归式的产生

    动规首先要有方向,所以把所有的粉刷匠根据s[i]排序。影响赚钱最大值的因素有选了哪几个人,以及人是怎么粉刷的。所以定义DP[i][j]为前i个粉刷匠负责到木板j时赚钱最大值。分情况:1.j没有被i刷(1)i什么木板都没刷(①)(2)i刷了一些木板,但没有刷到j(②)。2.j被i刷(③)。

    定义k为粉刷匠i-1所刷到的最后一个木板的位置,则总递归式为:

    DP[i][j] = max{①DP[i-1][j], ②DP[i][j-1], ③if(j>=S[i]) max foreach k(max(0, j-L[i])<=k<=S[i]-1) (DP[i-1][k] + P[i] * (j-k))}。

    单调队列优化

    当i固定时,我们要对每一个j,在一个已知区间[max(0, j-L[i]), S[i]-1]中求最值,区间随着j的增大在从左往右滑动,所以想到对每个i构造单调队列。队列中单调的只能有k,于是提出P[i]*j,将③变为P[i]*j+max{DP[i-1][k]+P[i]-k}。这样维护一个关于k,DP[i-1][k]+P[i]-k单调递减的单调序列即可达到优化的效果。

    注意

    • k值不应当定义为粉刷匠i从第k个木板开始刷,因为这样单调队列里的值关于k-1单调,翻来倒去导致了混乱。
    • 看以下代码:
      for (int k2 = max(0, s - len); k2 <= s; k2++)
              {
                  int k1 = q.front(); 
                  while (!q.empty() && DP[i - 1][k1] - p*k1 <= DP[i - 1][k2] - p*k2)
                      q.pop_front();
                  q.push_back(k2);
              }
      //s:s[i] p:p[i]

       这里错误非常多:

    1. 此处k1永远是循环刚开始的q.front(),此后一直都不变。
    2. k2<=s:循环轮到s-1时,s-1已被处置,不需要多处理一次s-1+1。
    3. 本循环是在处置队尾,所以是q.pop_back(),而不是q.pop_front()。

    完整代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdarg>
    using namespace std;
    
    #define LOOP(i,n) for(int i=1; i<=n; i++)
    
    void _printf_(const char * format, ...)
    {
    #ifdef _DEBUG
    	//va_list args;
    	//va_start(args, format);
    	//vprintf(format, args);
    	//va_end(args);
    #endif
    }
    
    const int MAX_BLOCK = 17000, MAX_WORKER = 110;
    int DP[MAX_WORKER][MAX_BLOCK];
    int totWorker, totBlock;
    
    struct IntDeque
    {
    	int a[MAX_BLOCK], head, tail;
    	void clear() { head = tail = 0; }
    	void push_back(int x) { a[tail++] = x; }
    	void pop_back() { tail--; }
    	void pop_front() { head++; }
    	bool empty() { return head == tail; }
    	int front() { return a[head]; }
    	int back() { return a[tail-1]; }
    	void Tranvas() { for (int i = head; i < tail; i++)_printf_("%d ", a[i]); _printf_("
    "); }
    };
    
    struct Worker
    {
    	int Start, Price, Len;
    	bool operator <(const Worker a)const
    	{
    		return Start < a.Start;
    	}
    }_workers[MAX_WORKER];
    
    int Dp()
    {//i:worker j:block
    	memset(DP, 0, sizeof(DP));
    	static IntDeque q;
    	LOOP(i, totWorker)
    	{
    		q.clear();
    		int p = _workers[i].Price, s = _workers[i].Start, len = _workers[i].Len;
    		for (int k2 = max(0, s - len); k2 <= s -1; k2++)
    		{
    			int k1 = 0; 
    			while (!q.empty() && DP[i - 1][k1=q.back()] - p*k1 <= DP[i - 1][k2] - p*k2)
    				q.pop_back();
    			q.push_back(k2);
    		}
    		LOOP(j, totBlock)
    		{
    			DP[i][j] = max(DP[i - 1][j], DP[i][j - 1]);
    			//q.Tranvas();
    			_printf_("DP[%d][%d]=%d
    ", i, j, DP[i][j]);
    			if (j >= s)
    			{
    				int len = j - _workers[i].Len;
    				while (!q.empty() && q.front() < len)
    					q.pop_front();
    				int k = q.front();
    				if (!q.empty())
    					DP[i][j] = max(DP[i][j], DP[i - 1][k] + p*(j - k));
    				//q.Tranvas();
    				_printf_("DP[%d][%d]=%d
    ", i, j, DP[i][j]);
    			}
    		}
    	}
    	return DP[totWorker][totBlock];
    }
    
    int main()
    {
    #ifdef _DEBUG
    	freopen("c:\noi\source\input.txt", "r", stdin);
    #endif
    	scanf("%d%d", &totBlock, &totWorker);
    	LOOP(i, totWorker)
    		scanf("%d%d%d", &_workers[i].Len, &_workers[i].Price, &_workers[i].Start);
    	sort(_workers + 1, _workers + totWorker + 1);
    	printf("%d
    ", Dp());
    	return 0;
    }
    

      

  • 相关阅读:
    ODAC配置
    mysql 创建索引和删除索引
    Linux下安装多个tomcat
    CentOS7/6 关闭防火墙
    从navicat中导入sql文件过大:Got a packet bigger than 'max_allowed_packet' bytes
    手动添加jar包到本地仓库
    mysql权限
    Linux常用命令
    centos 6.5安装VMware tools
    MySql 查询数据库中所有表名以及对比分布式库中字段和表的不同
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8490965.html
Copyright © 2020-2023  润新知