• 【ybtoj高效进阶 21282】数字重组(DP)(数学)


    数字重组

    题目链接:ybtoj高效进阶 21282

    题目大意

    给你一个数组,再给出一个数 k,保证数组长度是 k 的倍数。
    然后要你把数组分成 k 个集合,定义一个分法的价值是它 k 个集合的极差之和,然后要你找价值最小的分发,输出其价值。

    思路

    考虑从小到大枚举数字。

    考虑先把 (k) 个集合弄出来,以它们的个数弄成一个 vector 作为状态。
    那如果你接下来加的集合是空的,那我们就会有 (-a_i) 的贡献,如果你加了之后集合满了,就有 (a_i) 的贡献。(因为你数字是从小到大依次加入)
    那显然我们发现顺序不同的仍是同一个集合,所以我们可以排序,以减小状态数。

    但接着有一个问题,它要划分成集合,也就是不能有重复的数字被划入同一个集合中。
    那我们考虑设 (f_{i,j,S}) 为搞定到第 (i) 个数,当前状态集合是 (S),放 (i) 之前 (i) 这个位置的数所在集合的最小大小是 (j)。那我们就选当前个数不超过 (j) 的来放即可,因为如果之前有了那现在它的个数肯定是 (j+1)

    那我们就再转移,看看复杂度,首先要算的是集合的个数。
    它相当于从 ((0,0)) 走到 ((k,frac{n}{k})) 的折线。
    所以你就是把向右向上的操作随便放,就是组合数 (C_{k+frac{n}{k}}^k)

    不难看出当 (k=sqrt{n}) 的时候,状态数最多,为 (C_{2sqrt{n}}^{sqrt{n}})

    然后再乘上 (nk),复杂度可以过。

    代码

    #include<map>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    
    using namespace std;
    
    int n, k, a[101];
    map <vector<int>, ll> now, nxt;
    vector <int> emp;
    ll ans;
    
    int main() {
    //	freopen("num.in", "r", stdin);
    //	freopen("num.out", "w", stdout);
    	
    	scanf("%d %d", &n, &k);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    	
    	sort(a + 1, a + n + 1);
    	
    	emp.push_back(k);
    	for (int i = 1; i <= k; i++)
    		emp.push_back(0);
    	now[emp] = 0;
    	for (int i = 1; i <= n; i++) {
    		nxt.clear();
    		
    		for (map <vector<int>, ll> ::iterator it = now.begin(); it != now.end(); it++) {
    			vector <int> A = (*it).first;
    			ll B = (*it).second;
    			for (int j = 1; j <= k; j++) {
    				if (A[j] == n / k) continue;
    				if (a[i] == a[i - 1] && A[j] > A[0]) continue;
    				
    				vector <int> toA = A;
    				ll toB = B;
    				toA[j]++;
    				if (toA[j] == 1) toB -= a[i];
    				if (toA[j] == n / k) toB += a[i];
    				toA[0] = 0; sort(toA.begin(), toA.end());//记得重新排序
    				toA[0] = A[j];
    				
    				if (nxt.find(toA) == nxt.end()) nxt[toA] = toB;
    					else nxt[toA] = min(nxt[toA], toB);
    			}
    		}
    		
    		swap(now, nxt);//滚动数组
    	}
    	
    	ans = 1e15;
    	for (map <vector<int>, ll> ::iterator it = now.begin(); it != now.end(); it++) {
    		ans = min(ans, (*it).second);
    	}
    	printf("%lld", ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    集合操作
    聚合函数
    图存储3-十字链表
    图存储2-邻接表
    图存储1 临接矩阵
    字符串逆序,字符串翻转
    读写文件
    加密算法
    静态变量-动态变量
    【Qt】UserDefindeControl
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21282.html
Copyright © 2020-2023  润新知