zoukankan      html  css  js  c++  java
  • 【CF1420E】Battle Lemmings

    Description

    被保护的李明明:如果两只不持盾李明明中间的某个地方有至少一只迟钝的李明明
    那么这两只李明明就是一对被保护的李明明
    现在,旅鼠们会被命令交出自己手里的盾盾给它左边或者右边的旅鼠
    无情的命令机器 Petr 一秒可以发出一道命令

    对于所有可能的 (k)
    (k) 秒内((0le kle frac{n(n-1)}{2}))最多能够让旅鼠大军里有多少对被保护的李明明?
    李明明们可以脚踏很多条船。

    旅鼠大队 (1le nle80)


    Solution

    首先我读错题目辣
    一开始以为最后要变成 010010010 这样的
    实际上貌似是要最大化序列中,连续的 0 段落 的 两两长度乘积之和

    转化 #1

    那干脆直接用这些长度来表示序列。
    比如 010010010 就是表示成 1,2,2,1
    如果让第5只旅鼠把盾给左边就变成 1,1,3,1,再往左传就是 1,0,4,1 (也即 1,4,1

    (N) 表示这种 0 段落的个数。
    则操作过程中 0 段落两侧的 1 始终不变
    就算段落被压扁了又出现了,两侧的 1 也是原来的那两个

    转化 #2

    现在要求最大化 $$Ans=sumlimits_{i=1}{N}sumlimits_{j=1}{i-1} a_i a_j$$
    考虑一下怎么化简。求和表是一个三角形。
    复制一下填上是缺了主对角线的正方形。所以
    (egin{array}{rcl} Ans&=&displaystylefrac{1}{2}left[left(sumlimits_{i=1}^Nsumlimits_{j=1}^N a_i a_j ight)-sumlimits_{i=1}^N a_i^2 ight]\ &=&displaystylefrac{1}{2}left[left(sumlimits_{i=1}^N a_i ight)^2-sumlimits_{i=1}^N a_i^2 ight]end{array})

    思考意义。 (sumlimits_{i=1}^N a_i) 就是原序列中 0 的个数,为常数。
    故问题到这里转化为:

    最小化 (sumlimits_{i=1}^N a_i^2)


    DP #1

    最终的最优解就是让所有 (a_i) 的值尽可能相等。
    然而对于 (k) 时的最优解就有点难做了。
    (n) 很小。但是就算很小,假如一个一个枚举举盾不举盾 最坏情况最少这一步就要 (2^{40}) 也会炸的。

    DP #2 设计状态

    最优化,不妨考虑暴力 DP 。(n) 很小。所以 (k) 直接可以作为 DP 状态的一维。
    记录一下现在到第 (j)0 段落,命令了 (k) 次之后的最小的 (sumlimits_{i=1}^j a_i^2)

    考虑转移。因为每次命令都是让 1 左右两边的 0 个数一边 ++ 一边 --
    故:只需要记录 (ell=sumlimits_{i=1}^j a_i) 就可以了。
    这样左边的 01 个数都可以被确定 也就相当于确定了当前段落 1 转移后的位置。
    这就足够了,是一个正确的转移。

    状态: (f(k,j,ell)) 表示此时的,当前最小的 (sumlimits_{i=1}^j a_i^2)


    DP #3 设计转移

    无非就是枚举一下当前 0 段落右侧的 1 要走到哪里然后转移
    这个不难。
    我就直接写代码了,敲博客好累的(


    简明扼要的正解

    其实就是暴力……只要想到 DP 就可以做了
    从左到右依次确定每段连续的 0 的右端 1 的最后位置。同时转移时间和答案。
    剪一下枝过。
    1 的时候可以用前面 0 的个数代替距离,正确性显然。(独木桥一下)

    注意边界。
    要在 DP 的时候算出结果的话,最后应该推上最后那一段 0 并且保证枚举在末端结束
    保证枚举在末端结束:首先要让枚举都能够跑到末端。
    然后只需要最后输出答案的时候只考虑 (ell) 等于原序列 0 的总数的 DP 状态即可。


    Code


    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    using namespace std;
    const int MAXN = 100;
    const int MAN = 4000;
    int n, m = 0, N, Ze = 0, cnt = 0;
    bool a;
    int z[MAXN] = {};
    int PreZe[MAXN] = {};
    int DP[MAN][MAXN][MAXN] = {};
    void Update(int& a, int b)
    {
    	if (a==-1)
    	{
    		a = b; return;
    	}
    	if (b<a) a=b;
    }
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(0); cout.tie(0);
    	cin >> n;
    	N = n * (n - 1) >> 1;
    	for (int i = 1; i <= n; ++i)
    	{
    		cin >> a;
    		if(a) {
    			z[++m] = cnt;
    			Ze += cnt;
    			PreZe[m] = Ze;
    			cnt = 0;
    		}
    		else ++cnt;
    	}
    //	if (cnt)
    //	{
    		z[++m] = cnt;
    		Ze += cnt;
    		PreZe[m] = Ze;
    //	}
    	memset(DP, -1, sizeof(DP));
    	DP[0][0][0] = 0;
    	int ThisPara;
    	for (int k = 0; k <= N; ++k) // time
    	{
    		for (int j = 0; j < m; ++j) // Para M
    		{
    			for (int l = 0; l <= Ze; ++l) // Zeros
    			{
    				if (DP[k][j][l]==-1) continue;
    				for (int SZ = l, Walk; SZ <= Ze; ++SZ) // Zeroes To
    				{
    					Walk = k + abs(SZ - PreZe[j+1]); // Time To
    					if (Walk > N) continue; // Cut
    					ThisPara = SZ - l;
    					Update(DP[Walk][j+1][SZ], DP[k][j][l] + ThisPara * ThisPara);
    				}
    			}
    		}
    	}
    	
    	int Ans = 0x3f3f3f3f;
    	int ZeZe = Ze * Ze;
    	for (int i = 0; i <= N; ++i)
    	{
    		if (DP[i][m][Ze] != -1 && DP[i][m][Ze] < Ans) Ans = DP[i][m][Ze];
    		cout << ((ZeZe - Ans) >> 1) << ' ';
    	}
    	return 0;
    }
    
  • 相关阅读:
    简练网软考知识点整理-项目选择和优先级排列方法
    简练网软考知识点整理-项目基线
    简练网软考知识点整理-项目质量控制七工具之排列图
    简练网软考知识点整理-项目经理应具备的技能能力
    简练网软考知识点整理-项目招投标相关法律
    Scala集合库、模式匹配和样例类
    Scala函数式编程
    Scala面向对象—类详解2(继承相关)
    gVerify验证码
    Scala面向对象—类详解
  • 原文地址:https://www.cnblogs.com/ccryolitecc/p/13763915.html
Copyright © 2011-2022 走看看