Codeforces Round #717 (Div. 2)-C. Baby Ehab Partitions Again-题解

100 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

@TOC

Codeforces Round #717 (Div. 2)-C. Baby Ehab Partitions Again

传送门 Time Limit: 2 seconds Memory Limit: 256 megabytes

Problem Description

Baby Ehab was toying around with arrays. He has an array aa of length nn. He defines an array to be good if there's no way to partition it into 22 subsequences such that the sum of the elements in the first is equal to the sum of the elements in the second. Now he wants to remove the minimum number of elements in aa so that it becomes a good array. Can you help him?

A sequence bb is a subsequence of an array aa if bb can be obtained from aa by deleting some (possibly zero or all) elements. A partitioning of an array is a way to divide it into 22 subsequences such that every element belongs to exactly one subsequence, so you must use all the elements, and you can't share any elements.

Input

The first line contains an integer nn (2n1002 \le n \le 100) — the length of the array aa.

The second line contains nn integers a1a_1, a2a_2, \ldots, ana_{n} (1ai20001 \le a_i \le 2000) — the elements of the array aa.

Output

The first line should contain the minimum number of elements you need to remove.

The second line should contain the indices of the elements you're removing, separated by spaces.

We can show that an answer always exists. If there are multiple solutions, you can print any.

Sample Input

4
6 3 9 12

Sample Onput

1
2

Note

In the first example, you can partition the array into [6,9][6,9] and [3,12][3,12], so you must remove at least 11 element. Removing 33 is sufficient.

In the second example, the array is already good, so you don't need to remove any elements.


题目大意

给你nn个数,让你去掉尽可能少的数,使得剩下的数不能被分成和相等的两份。 和相等的两份是指第一份中的所有的元素的和等于第二份中所有的元素的和。


输出描述

这一题保证必定有解。

第一行输出至少需要去除的数的个数 第二行输出要去除的这些数的下标。


解题思路

这题答案只能是0011

  • 如果本来就不能分成两份,那么只需要去除00个数,即答案是00
  • 如果本来能够分成和相等的两份,那么就说明这些数的和是偶数(这些数的和是奇数的话,是不可能分成和相等的两组的)。然后就看这些数中有没有一个奇数。
    • 如果这些数中包含奇数,我们只需要把这一个奇数去掉,剩下的数的和就变成了奇数,就不能分成和相等的两组。
    • 否则这些数中都是偶数的话,就所有数分别除以22,不影响分配结果(如果除以22之前能分成和相等的两组的话,那么都除以22之后他们的和还相等)。直到含有奇数为止,就去掉这个奇数,即为答案。

那么问题就是如何快速确定最初的数组能不能分成连个和相同的数组,用01背包即可解决。

我们用一个boolbool类型的数组dp[i][j]dp[i][j]来表示前ii个数的和能不能是jj。 那么当前i1i-1个数的和能为jj时,就不加上这个(第ii个)数,和还是jj。 或者前i1i-1个数的和正好能组成j这个(第j-这个(第i个)数的值个)数的值,那么加上这个数,总和正好是jj。 状态转移方程:dp[i][j]=dp[i-1][j]|dp[i-1][j-a[i]]


AC代码

#include <bits/stdc++.h>
using namespace std;
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;
int a[111]={0};
bool dp[111][111111]={0};//dp[i][j]表示前i个能不能正好为j
int main()
{
    int n;
    cin>>n;
    int sum=0;//sum代表这n个数的总和
    fi(i,0,n)//for(int i=0;i<n;i++)
        cd(a[i]),sum+=a[i];//scanf("%d", &a[i]),sum+=a[i]
    if(sum%2)//如果总和是奇数,直接就不能分成和相等的两组,去掉0个数即可
    {
        puts("0");
        return 0;
    }
    sum/=2;//否则要找这n个数中,能不能找到一些数,他们的和是sum/2。如果能找到,就说明可以分成相等的两组
    for(int i=0;i<n;i++)//初始化dp数组
    {
        dp[0][a[i]]=1;
    }
    for(int i=1;i<=n;i++)//前i个数
    {
        for(int j=sum;j>=a[i];j--)//和能不能是j
        {
            dp[i][j]=dp[i-1][j]|dp[i-1][j-a[i]];
        }
    }
    if(!dp[n][sum])//如果原本的数就不能分成和相等的2份
    {
        puts("0");//直接去掉0个元素即可
        return 0;
    }
    puts("1");//否则需要去掉一个元素
    while(1)
    {
        fi(i,0,n)//for(int i=0;i<n;i++)
        {
            if(a[i]%2)//找到了一个奇数
            {
                printf("%d\n",i+1);//去掉这个奇数,剩下的数的和就是奇数,就不能分成两组了
                return 0;
            }
        }
        fi(i,0,n)//全部除以2
        {
            a[i]/=2;
        }
    }
    return 0;
}

同步发文于我的CSDN,原创不易,转载请附上原文链接哦~
Tisfy:letmefly.blog.csdn.net/article/det…