TCO2012Round1B-3-FoxAndPhotography

来源:互联网 发布:ubuntu 服务管理 编辑:程序博客网 时间:2024/05/11 15:35
zz:http://www.strongczq.com/2012/04/tco2012round1b-3-foxandphotography.html
题目原文:
http://community.topcoder.com/stat?c=problem_statement&pm=11809&rd=15091

题目大意:
     两排人一起合影,前一排的人身高用int[] heightsFront表示,后排的人身高用int[] heightsBack表示,如果对应位置前排人身高大于等于后排人身高,则会发生遮挡现象。限制只能交换两个相邻的人的位置,问至少需要多少次交换才能使得没有遮挡现象,如果没有可行方案则返回-1。
     数据规模:每排人数[2,16], 身高范围[140,190]

思路:
     显然交换前排两人的位置等价于交换后排对应两人的位置,所以只需要考虑在某一排内做相邻位置交换即可,这里只考虑前排的交换。需要观察到一个性质:
     性质1:通过交换相邻元素将数列(1,2,...,n)转换成(i1,i2,...,in),一种交换次数最少的交换方案为分别按序将i1, i2, ...,in交换到目标位置。
     证明:
          对于任意一个元素i(j),为了将其交换到第j个位置,那么其必然与满足以下条件的元素各发生了一次使其左移的交换:
    • 该元素在原序列中在i(j)的左边
    • 该元素在目标序列中在i(j)的右边。
          以上所描述的交换方式中,当开始交换i(j)时,第j个位置到i(j)所在位置之间的所有元素正好就是符合以上条件的元素,i(j)只与这些元素发生过左     移交换。所以该交换方案中,对于任意一个元素其左移交换的次数都是最少的,那么所有元素的左移次数总和也是最少的。显然每次交换必然有元素发生左移,所以交换次数与左移次数必然相等,那么交换次数也是最少的。证毕。
          
     假设每排的人数为n。根据性质1,我们考虑前排的第一个位置,最优方案中前排所有身高小于后排第一个位置的人都可能在该位置,因此我们需要遍历所有的这些情况并记录下相应的交换次数。然后再从第二个位置开始,同样考虑第二个位置的可能结果,依次类推考虑到最后一个位置。显然该迭代过程存在重复子问题,可以使用动态规划求解。提取这中间的重复子问题:当考虑的x位置时,已经有x-1个人的位置是已经确定的,由于这x-1个人的具体位置选择对后续的决策没有任何影响,所以所有该状态下的子问题可以一并求解,通过二进制掩码可以表示这个状态,总共有2^n个子问题。因此动态规划的状态变量为当前考虑的位置x,以及一个二进制掩码m表示已确定位置的人。进一步的,x的值可以根据m中1的个数来确定,所以只需要m就可以表示状态。状态转移方式为:考虑m状态时,对所有未确定位置的人,如果可以移动到当前位置,则将其移动过来,考虑所有可行方案取最优值。算法时间复杂度为O(n*2^n)。
          


Java代码:
public class FoxAndPhotography {    public int getMinimumSwaps(int[] heightsFront, int[] heightsBack) {        int n = heightsBack.length ;        int[] dp = new int[1 << n];        for (int m = (1 << n) - 2; m >= 0; --m) {            dp[m] = Integer. MAX_VALUE;            int oneNum = numOfOne(m);            int c = 0;            for (int i = 0; i < n; ++i) {                if (((1 << i) & m) == 0) {                    if (heightsFront[i] < heightsBack[oneNum]                            && dp[m | (1 << i)] != Integer. MAX_VALUE) {                        dp[m] = Math. min(dp[m], dp[m | (1 << i)] + c);                    }                    c++;                }            }        }        return dp[0] == Integer.MAX_VALUE ? -1 : dp[0];    }    private int numOfOne(int m) {        int res = 0;        while (m != 0) {            m &= (m - 1);            res++;        }        return res;    }}



原创粉丝点击