Tuesday, March 24, 2015

UVa 11088 - End up with More Teams

Problem Statement:
11088 - End up with More Teams


Solution:
This problem looks innocent enough, until it bit me with a few WA and TLE. The idea to solve this problem is by using a bitmask DP technique, which I will elaborate. This solution took \( O(kN2^N) \) time.

Firstly let B be the set for which \( i \in B\) if and only if i-th person is already chosen as a member of some team. Let a[i] be the "ability" value of i-th person, we know that a[i] \( \leq 30\). Lastly, let D[B][val] be the maximum number of groups that can be formed using people in B, and such that if the total number of people in B is not divisible by 3, val will be the total ability of the extra people in B not in any group yet. For every j not in B yet, we do:

1. Let B' be the set formed after adding j into B
2. For every val, update D[B'][val+a[j]] to D[B][val] if larger.
3. In the case that B is divisible by 3, we do the following check:
    - Amongst all val in [0..19], store the maximum of D[B][val] as prev. This means that the last 3 person in B can only have a total ability of at most 19, hence cannot be a group.
    - Amongst all val in [20..90], store the maximum of D[B][val] as cur. This means that the last 3 person in B can form a group with total ability at least 20.
    - Update D[B][0] to max {prev, cur+1}.

To cut for the time limit, you can group all D[B][val] for all val in [20..90] into one compartment, hence reduces the search time by almost 100 times.

Implementation:


#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;



int dp[1<<15][100];
int a[20];
int N;
void solve(){
    for(int b=0;b<(1<<15);++b){
        for(int i=0;i<100;++i){
            dp[b][i] = -1;
        }
    }
    dp[0][0] = 0;
    int ans = 0;
    for(int K=0;K<=N;++K){
        for(int b=0;b<(1<<N);++b){
            int n = 0;
            for(int i=0;i<N;++i) if(b & (1<<i)) ++n; 
            if(n!=K)continue;
            if(n!=0 && n%3==0){
                int cnt = dp[b][20];
                dp[b][20] = -1;
                int prev = -1;
                for(int i=0;i<=19;++i) {
                    prev = max(dp[b][i], prev);
                    dp[b][i] = -1;
                }
                dp[b][0] = max(cnt+1, prev);
                ans = max(dp[b][0], ans);
            }
            for(int i=0;i<=20;++i){
                if(dp[b][i]!=-1){
                    for(int j=0;j<N;++j){
                        if(b & (1<<j)) continue;
                        dp[b|(1<<j)][min(20, i+a[j])] = max(dp[b|(1<<j)][min(20,i+a[j])], dp[b][i]);
                    }
                }
            }
        }
    }
    printf("%d\n", ans);
}


int main(){
    int tc=1;
    while(scanf("%d",&N), N!=0){
        for(int i=0;i<N;++i)scanf("%d",&a[i]);
        printf("Case %d: ", tc++);
        solve();
    }
    return 0;
}