zoukankan      html  css  js  c++  java
  • Cookies题解

    来源:《算法竞赛进阶指南》

    Description:

    有M块饼干要分给N个孩子。当有k个孩子分到的饼干数比第i个孩子分到的多时,会产生g[i]*k的贡献。求最小的贡献及任意一种方案。

    Solution:

    根据不同的饼干数分给不同的孩子会产生不同的贡献值,而题目让我们求最小的贡献值,很显然我们就能想到可以用动态规划来解决。

    根据贪心的思想:
    『当g[i] <g[j]时,对于剩下所有的孩子来说,使a[i]>=a[j],产生的贡献值一定是最小的。』

    我们先将序列g进行从大到小排序。接下来进行状态转移,

    当枚举到第i个孩子,已经分了j块饼干时:

    1.当第i个孩子分到的饼干数>=i时,我们可以将ta分到的饼干数里拿出来i-1块,来分给前i-1个人,这样对前面所有孩子产生的贡献不会发生改变;(时间复杂度O(1))

    2.若第i个孩子分到的饼干数为1时,我们可以假设前面也有孩子分到了1块饼干,该状态的贡献可以由第k个孩子已经分了j-i+k块饼干进行转移;(时间复杂度O(N))

    3.1<若第i个孩子分到的饼干数<i时,不进行转移。(时间复杂度O(0))

    所以我们可以得到:(f[0][0]=0)

    f[i][j] = min{f[i][j-i],min(0<=k<i){f[k][j-i+k]+k*cigma(p[k+1]...p[i])}}.

    但是题目还让我们求方案数,则我们可以开一个数组来记录当前装填是由哪一个状态转移而来的。进行dfs时,当i=0且j=0时,递归结束。

    对于一个二元组x,y,不妨设它是由x0,y0转移而来,则

    先递归到最底层:

    1.当x=x0时,f[i][j]由f[i][j-i]转移而来,所以我们将1到x-1的数都加1;

    2.当x>x0时,f[i][j]由f[k][j-i+k]转移而来,则第x0+1到第i的数都是1.

    Code:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 33, M = 5050;
    
    int n, m; 
    struct children{
    	int num, p;
    }a[N];
    
    bool cmp(children x, children y) {
    	return x.p > y.p;
    }
    
    inline void init() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++ i) scanf("%d", &a[i].p), a[i].num = i;
    	sort(a + 1, a + n + 1, cmp);
    	for (int i = 1; i <= n; ++ i) a[i].p += a[i - 1].p;
    }
    
    inline int cigma(int l, int r) {
    	return a[r].p - a[l - 1].p;
    }
    
    int f[N][M], pre1[N][M], pre2[N][M];
    //the least value of before i-th child, has already given out j pieces of cookies
    
    void solve1() {
    	memset(f, 0x3f, sizeof(f));
    	f[0][0] = 0;
        for (int i = 1; i <= n; ++ i) {
        	for (int j = i; j <= m; ++ j) {
        		f[i][j] = f[i][j - i];
        		pre1[i][j] = i;
        		pre2[i][j] = j - i;
        		for (int k = 0; k < i; ++ k) {
        			int sum = k * cigma(k + 1, i);
        			if (f[k][j - i + k] + sum < f[i][j]) {
        				f[i][j] = f[k][j - i + k] + sum;
        				pre1[i][j] = k;
        				pre2[i][j] = j - i + k;
    				}
    			}
    		}
    	}
    }
    
    int ans[N];
    
    void dfs(int x, int y) {
    	if (x == 0 && y == 0) return ;
    	int x0 = pre1[x][y], y0 = pre2[x][y];
    	dfs(x0, y0);
    	if (x == x0) {
    		for (int i = 1; i <= x; ++ i) 
    			++ ans[a[i].num];
    	}
    	else {
    		for (int i = x0 + 1; i <= x; ++ i) 
    			++ ans[a[i].num];
    	}
    }
    
    void solve2() {
    	dfs(n, m);
    }
    
    inline void work() {
    	solve1();
    	solve2();
    }
    
    inline void outo() {
    	printf("%d
    ", f[n][m]);
    	for (int i = 1; i <= n; ++ i) printf("%d ", ans[i]);
    	puts("");
    }
    
    int main() {
        init();
        work();
        outo();
        return 0;
    }
    
  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/ckn1023/p/10949827.html
Copyright © 2011-2022 走看看