zoukankan      html  css  js  c++  java
  • #3342. 「NOI2020」制作菜品

    题目链接

    考场上想到 70pts,结果没清空+没特判转移来源丢掉 35pts

    不那么显然,如果 (m ge n - 1),那么一定有解。

    考虑如果只剩下一堆大于 (k) 的材料,那么每次我们取一个,不够再取另一个,一定能取完。于是我们只用考虑怎么消灭小于 (k) 的材料。因为每次我们可以把最小的那个取走,不够再从大于 (k-v_i) 的那些材料里面找,每取一次肯定能消灭一个小的,并且我们能保证能找到大于 (k-v_i) 的材料,否则剩下的平均将小于 (k-v_i),仔细想想发现 (m ge n - 1) 的时候不可能。因此我们能够在 (m ge n - 1) 的情况下找到解。

    现在考虑 (m = n - 2)。如果有解,一定时拿着拿着发现正好有两个的和为 (k),一下子消掉两个。换言之,我们能够将原材料分成两个集合,对于每个集合都符合 (sum_{i in S} v_i = (|S| - 1) * k) 的性质。那么我们直接背包 DP 即可拿到 70pts。

    发现我们要记一个集合大小,很不方便。我们可以统一让 (v_i) 减去 (k),最后判断是否可能为 (-k) 即可。

    发现我们的 DP 状态设成布尔类型肯定不好,并且发现每次就是对数组进行左移和或操作,因此直接上 bitset 优化即可。因为我们要知道转移路径,因此不能滚动数组,但是在 bitset 的强力优化下还是可行的(DP数组大概有几千万字节)。不过绝对不能记录 (pre),要当场手动模拟,看如果选择 (cur) 之前的状态是否合法,据此决定是否选择 (cur)

    关键代码:

    bool vis[567];
    inline void Get_ans() {
    	memset(vis, 0, sizeof(vis));
    	for (register int i = 1; i <= stop; ++i) {
    		nd[i] = (node){stk[i], d[stk[i]]};
    		vis[stk[i]] = true;
    	}
    	sol(stop, stop - 1);
    	int ntot = 0;
    	for (register int i = 1; i <= n; ++i) {
    		if (!vis[i])	nd[++ntot] = (node){i, d[i]};
    	}
    	sol(ntot, ntot - 1);
    	Success();
    }
    
    bitset<NN> bt[505];
    inline bool Try() {
    	bt[0][base] = true;
    	for (register int i = 1; i <= n; ++i) {
    		int cha = d[i] - k;
    		bt[i] = bt[i - 1] | (cha > 0 ? bt[i - 1] << cha : bt[i - 1] >> (-cha));
    	}
    	if (!bt[n][-k + base])	return false;
    	int nw = -k;
    	for (register int i = n; i; --i) {
    		int cha = d[i] - k;
    		if (nw - cha + base >= 0 && bt[i - 1][nw - cha + base]) {
    			stk[++stop] = i;
    			nw -= cha;
    		}
    	}
    	return true;
    }
    
  • 相关阅读:
    NOP源码分析六--实体、数据的分层与处理。
    NOP源码分析七---继续
    NOP源码分析 八---set的存储
    Nop 源码分析四 任务系统
    NOP源码分析五,文件位置等详细内容,感冒真难受,嗓子痒又疼。。
    1
    mobx
    ts随笔
    13.vue-vuex
    13.vue-axios
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13537553.html
Copyright © 2011-2022 走看看