zoukankan      html  css  js  c++  java
  • 【CodeForces 453B】Little Pony and Harmony Chest

    链接:

    洛谷

    博客园

    题目大意:

    给定一个数组 (a),找到一个两两互质的数组 (b) 使得 (sumlimits_{i=1}^{n}|a_i-b_i|) 最小。

    正文:

    我们可以从 (a_ileq 30) 想到这道题可能与状压沾边。设 (f_{i,J}) 表示前 (i) 个数选了集合 (J) 中的质数作为 (b_i) 的质因数。其中集合 (J) 在实现中就是那么有:

    [f_{i,J}=min_{Ksubseteq J,k=|J-K|}{f_{i-1,K}+|a_i-k|} ]


    但是还有一个问题:我们不知道 (b_i) 的范围。

    这个问题也好解决。还是 (a_ileq 30),因为 (b_i) 最小是 (1),那么 (|a_i-b_i|) 的上界是 (30-1=29)。也就是说 (b_i) 不超过 (30+29=59),又因为差值如果是 (29) 的话,(b_i=1) 会更优,毕竟 (1) 可以重复选择。所以实际上 (b_i) 不超过 (58)

    以上是我的思考过程,仅供参考。如果要看更严谨的证明,可以移步题解区看 @周子衡 爷的题解

    代码:

    我代码里 (b_i) 的上界还是 (59) 的,可以改成 (58)

    int pri[20] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59}; 
    
    void Output(int x, int y)
    {
    	if (!x) return;
    	Output(x - 1, y ^ has[ans[x][y]]);
    	printf ("%d ", ans[x][y]);
    }
    
    int main()
    {
    //	freopen(".in", "r", stdin);
    //	freopen(".out", "w", stdout);
    	scanf ("%d", &n);
    	for (int i = 1; i <= n; ++i)
    		scanf ("%d", &a[i]);
    	for (int i = 1; i <= 59; i++)
    		for (int j = 0; j < 17; j++)
    			if(!(i % pri[j])) has[i] |= 1 << j;
    	for (int i = 1; i <= n; i++)
    	{
    		f[i][0] = 1e9;
    		for (int j = 0; j < (1 << 17); f[i][++j] = 1e9)
    			for (int k = 1, tmp = 0; k <= 59; k++)
    			{
    				if ((has[k] | j) != j) continue;
    				tmp = abs(a[i] - k) + f[i - 1][j ^ has[k]];
    				if (tmp < f[i][j]) f[i][j] = tmp, ans[i][j] = k;
    			} 
    	}
    	Output(n, (1 << 17) - 1); 
        return 0;
    }
    
  • 相关阅读:
    Android 走向MD的配色风格
    Android热点回顾第六期
    C#集合类:动态数组、队列、栈、哈希表、字典(转)
    Google Chrome默认字体设置(Win)
    C# Socket的粘包处理(转)
    设计模式原则总结--读《大话设计模式》有感 <转>
    C#设计模式学习笔记-单例模式(转)
    C# 编写Windows Service(windows服务程序)
    C# 获取农历日期
    C# 中怎么将string转换成int型
  • 原文地址:https://www.cnblogs.com/GJY-JURUO/p/14453716.html
Copyright © 2011-2022 走看看