페이지

레이블이 Bit Mask인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Bit Mask인 게시물을 표시합니다. 모든 게시물 표시

5842번: Partitioning the Farm

https://www.acmicpc.net/problem/5842

먼저 가로 방향 울타리의 위치를 정한다. 여기서 가능한 모양의 경우의 수는 $2^{n-1}$일 것이다.
그 다음, 남은 울타리를 세로 방향으로 설치하되 그룹 안 소의 최대 수가 최소가 되도록 설치하는 법을 찾아야 한다.
그룹 안 소의 최대 수를 x라고 하고 이 값에 대해 파라메트릭 서치를 한다. 농장의 왼쪽 열부터 보면서 그룹 안 소의 최대 수가 x이하가 되도록 세로로 울타리를 세웠을 때 모든 울타리 수가 k개 이하인지 판단하는 문제로 바꿔 풀 수 있다.

시간복잡도는 $O(2^{n/2}*n^2*\lg L)$

#include<cstdio>
int n, k, s[16][16], a[16], sz, mid, res = 1e9;
bool f() {
    int t = 0, cnt = 0;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= sz; j++) while (s[a[j]][i] - s[a[j]][t] - s[a[j - 1]][i] + s[a[j - 1]][t] > mid) {
            if (t == i - 1) return false;
            t = i - 1, cnt++;
        }
    }
    return cnt <= k - sz + 1;
}
int main() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) {
        scanf("%d", s[i] + j);
        s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
    }
    for (int i = 1 << n - 1; i--;) {
        sz = 0;
        for (int j = 0; j < n - 1; j++) if (1 << j&i) a[++sz] = j + 1;
        a[++sz] = n;
        int low = 0, up = 1e6;
        while (low <= up) {
            mid = low + up >> 1;
            f() ? up = mid - 1 : low = mid + 1;
        }
        if (res > up) res = low;
    }
    printf("%d", res);
    return 0;
}

13162번: Flowey's Love

https://www.acmicpc.net/problem/13162

알갱이들이 직선 방향으로 움직일 때 주어진 구역 안에서 돌아다니며 모을 수 있는 최대 알갱이 수를 구하는 문제이다.

대충보면 TSP 이기 때문에 다항시간 안에 풀기 힘들다는 것을 알 수 있다. 주어진 n이 작으므로 모든 상태를 따져보는 DP로 해결한다.
알갱이를 최대한 많이 모으는 방법은 알갱이를 최대한 빠르게 모으는 방법이기도하다. 이에 따라 dp를 다음과 같이 정의한다.
dp[i][j]: 모은 알갱이의 집합이 i이고 영혼이 알갱이 j에 위치해 있을 때 걸리는 최소 시간
dp[i][j] = min(dp[i - j][k] + (dp[i - j][k]시간이 흐른 후 k->j로 가는데 걸리는 최소 시간) : k$\in$i)
* i-j는 집합 i에서 j를 제외함을 의미함.

문제에서는 영혼이 돌아다닐 수 있는 구역이 제한되어 있기 때문에 이를 고려해야 한다. 자세한 구현은 아래 소스를 참고하자.

시간복잡도는 $O(n^2 * 2^n)$

#include<cstdio>
#include<algorithm>
#define eps(u,v) (abs((u)-(v))<1e-9)
#define cmp(u,v) (u<v || eps(u,v))
using namespace std;
int n, xm, ym, res;
double sx[19], sy[19], dp[1 << 19][19], vx[19], vy[19], lt[19];
double f(double x, double y, int h, double t) {
    double xi = sx[h] + vx[h] * t - x, yi = sy[h] + vy[h] * t - y;
    if (eps(xi, 0) && eps(yi, 0)) return 0;
    double a = (xi*vx[h] + yi*vy[h]) * 2, b = xi*xi + yi*yi;
    return eps(a, 0) || cmp(0, b / a) ? 1e5 : -b / a;
}
int main() {
    scanf("%d%d%d", &n, &xm, &ym); n++;
    for (int i = 1, ex, ey; i < n; i++) {
        scanf("%lf%lf%d%d", sx + i, sy + i, &ex, &ey);
        double d = hypot(ex - sx[i], ey - sy[i]);
        vx[i] = (ex - sx[i]) / d;
        vy[i] = (ey - sy[i]) / d;
        lt[i] = max(eps(vx[i], 0) ? 0 : min((-xm - sx[i]) / vx[i], (xm - sx[i]) / vx[i]),
            eps(vy[i], 0) ? 0 : min((-ym - sy[i]) / vy[i], (ym - sy[i]) / vy[i]));
    }
    fill(dp[0], dp[1 << n], 1e5);
    dp[1][0] = 0;
    for (int i = 1; i < 1 << n; i++) {
        int bt = 0;
        for (int j = i; j; j &= j - 1) bt++;
        for (int j = 0; j < n; j++) if (1 << j&i) {
            for (int k = 0; k < n; k++) if (1 << k&i &&j^k) {
                double t = dp[i ^ 1 << j][k], ret = max(t + f(sx[k] + vx[k] * t, sy[k] + vy[k] * t, j, t), lt[j]),
                    tx = sx[j] + vx[j] * ret, ty = sy[j] + vy[j] * ret;
                if (cmp(-xm, tx) && cmp(tx, xm) && cmp(-ym, ty) && cmp(ty, ym)) dp[i][j] = min(dp[i][j], ret);
            }
            if (dp[i][j] < 1e4) res = max(res, bt - 1);
        }
    }
    printf("%d", res);
    return 0;
}

13941번: Kronican

https://www.acmicpc.net/problem/13941

풀이는 http://hsin.hr/coci/contest3_solutions.zip 3번 참조

시간복잡도는 $O(n^2*2^n)$

#include<cstdio>
#include<algorithm>
using namespace std;
int n, k, c[20][20], dp[1 << 20];
int main() {
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) scanf("%d", c[i] + j);
    for (int i = 1 << n; i--;) {
        dp[i] = 1e9;
        int cnt = 0;
        for (int x = 0; x < n; x++) if (!(1 << x&i)) {
            for (int y = 0; y < n; y++) if (!(1 << y&i) && x^y) dp[i] = min(dp[i], dp[i | 1 << x] + c[x][y]);
            cnt++;
        }
        if (cnt <= k) dp[i] = 0;
    }
    printf("%d", dp[0]);
    return 0;
}

2803번: KOŠARE

https://www.acmicpc.net/problem/2803

$O(m(n+2^m))$

풀이:
http://hsin.hr/coci/archive/2011_2012/contest6_solutions.zip
6번 참조

#include<cstdio>
#define mod int(1e9+7)
int n, m, r;
long long c[1 << 20];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1 << m; i--;) c[i]++;
    while (n--) {
        int k, x, tot = 0;
        for (scanf("%d", &k); k--;) {
            scanf("%d", &x);
            tot += 1 << x - 1;
        }
        c[tot] = c[tot] * 2 % mod;
    }
    for (int i = 1; i < 1 << m; i *= 2)
        for (int j = 1 << m; j--;) if (i&j) c[j] = c[j] * c[j^i] % mod;
    for (int i = 1 << m; i--;) {
        int b = 0;
        for (int j = i; j; j &= j - 1) b++;
        r = (r + mod + c[i] * (m - b & 1 ? -1 : 1)) % mod;
    }
    printf("%d", r);
    return 0;
}

5923번: Binary Sudoku

https://www.acmicpc.net/problem/5923

$O(...)$

dp[i][j][k]: 1~i번 행을 xor한 값이 j이고 i번 행을 포함하는 세 개의 3*3 블록에 대해 각 블록마다 xor해서 표현한 세 자리 수가 k일 때, 토글 횟수의 최솟값

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int dp[10][1 << 9][1 << 3], a[10];
int f(int x, int y, int z) {
    if (!x) return y ? 99 : 0;
    if (x % 3 == 0 && z) return 99;
    if (dp[x][y][z] ^ -1) return dp[x][y][z];
    int ret = 99;
    for (int i = 1 << 9; i--;) {
        int t = 0, u = 0, v = 0;
        for (int j = 0; j < 9; j++) if (1 << j&i) t ^= 1 << j / 3, u++;
        if (u & 1) continue;
        for (int j = 0; j < 9; j++) if (1 << j&(i^a[x])) v++;
        ret = min(ret, f(x - 1, y^i, z^t) + v);
    }
    return dp[x][y][z] = ret;
}
int main() {
    memset(dp, -1, sizeof(dp));
    for (int i = 1; i <= 9; i++)
        for (int j = 0, x; j < 9; j++) scanf("%1d", &x), a[i] = a[i] * 2 + x;
    printf("%d", f(9, 0, 0));
    return 0;
}

2800번: ZAGRADE

https://www.acmicpc.net/problem/2800


$O(ln2^n)$

stack으로 괄호쌍을 찾아준 다음, 비트마스크를 이용해 가능한 모든 문자열을 구하고 사전순 정렬한다.

#include<iostream>
#include<string>
#include<set>
using namespace std;
string s;
int cnt, stk[10], top;
pair<intint> p[10];
set<string> st;
int main() {
    cin >> s;
    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '(') p[stk[top++] = cnt++].first = i;
        if (s[i] == ')') p[stk[--top]].second = i;
    }
    for (int i = 1 << cnt; --i;) {
        int ck[200] = {};
        for (int j = 0; j < cnt; j++) if (i & 1 << j) ck[p[j].first] = ck[p[j].second] = 1;
        string r;
        for (int j = 0; j < s.size(); j++) if (!ck[j]) r += s[j];
        st.insert(r);
    }
    for (auto it : st) cout << it << endl;
    return 0;
}

1819번: 불끄기

https://www.acmicpc.net/problem/1819


$O((l^2+t)2^t)$

dp를 통해 (여태 본 비트 길이, 최근 t-1개의 비트) 마다 최소 1의 개수를 구해 놓는다.

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int dp[51][1 << 6], l, t, m, b, c[1 << 7], a[51];
vector<int> v[51][1 << 6];
int main() {
    scanf("%d%d", &l, &t);
    for (int i = 1; i <= l; i++) scanf("%1d", a + i);
    for (int i = 0, x; i < t; i++) {
        scanf("%1d", &x);
        m = m * 2 + x;
        b = b * 2 + a[i];
    }
    for (int i = 1 << t; i--;)
        for (int j = i; j; j &= j - 1) c[i]++;
    fill(&dp[0][0], &dp[50][1 << 6], 1e9);
    dp[t - 1][b] = c[b];
    for (int i = t - 1; i < l; i++) {
        for (int j = 1 << t - 1; j--;) {
            int p = (j * 2 + a[i + 1] ^ m) % (1 << t - 1);
            if (dp[i + 1][p]>dp[i][j] - c[j] + c[j * 2 + a[i + 1] ^ m]) {
                dp[i + 1][p] = dp[i][j] - c[j] + c[j * 2 + a[i + 1] ^ m];
                v[i + 1][p] = v[i][j];
                v[i + 1][p].push_back(i - t + 2);
            }
            p = (j * 2 + a[i + 1]) % (1 << t - 1);
            if (dp[i + 1][p]>dp[i][j] - c[j] + c[j * 2 + a[i + 1]]) {
                dp[i + 1][p] = dp[i][j] - c[j] + c[j * 2 + a[i + 1]];
                v[i + 1][p] = v[i][j];
            }
        }
    }
    int r = min_element(dp[l], dp[l] + (1 << t - 1)) - dp[l];
    printf("%d", v[l][r].size());
    for (auto it : v[l][r]) printf("\n%d", it);
    return 0;
}

1759번: 암호 만들기

https://www.acmicpc.net/problem/1759


$O(c∗2^c)$

모든 경우를 조사한다.


#include<cstdio>
#include<algorithm>
int l, c;
char s[15], r[16];
int main() {
    scanf("%d %d", &l, &c);
    for (int i = 0; i < c; i++) scanf(" %c", &s[i]);
    std::sort(s, s + c);
    for (int i = 1 << c; --i;) {
        int p = 0, q = 0;
        for (int j = 0; j < c; j++) if (i & 1 << c - 1 - j)
            r[p++] = s[j], q += s[j] == 'a' || s[j] == 'e' || s[j] == 'i' || s[j] == 'o' || s[j] == 'u';
        if (p == l && q && p - q>1) r[l] = 0, puts(r);
    }
    return 0;
}

13134번: Baseball Watching

https://www.acmicpc.net/problem/13134


$O(?)$

9회까지 선생님이 취할 수 있는 이동 가지수는 3^9이다.
각 경우마다 선생님을 한 번도 만나지 않을 방법 2^9가지를 모두 조사한다.


#include<cstdio>
int n, c[19683], mini = 1e9, maxi;
int main() {
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        int t = 0;
        for (int j = 0, x; j < 9; j++) scanf("%d", &x), t = t * 3 + x - 1;
        c[t]++;
    }
    for (int i = 0; i < 19683; i++) {
        int s = 0;
        for (int j = 0; j < 1 << 9; j++) {
            int t = 0;
            for (int k = 0, ti = i, tj = j; k < 9; k++, ti /= 3, tj /= 2) t = t * 3 + (ti % 3 + tj % 2) % 3;
            s += c[t];
        }
        if (mini > s) mini = s;
        if (maxi < s) maxi = s;
    }
    printf("%d %d", mini, maxi);
    return 0;
}

8903번: 장비

https://www.acmicpc.net/problem/8903


$O(tn)$

최적해가 주어졌을 때, 최적해를 이루는 각 능력치의 값은 min(k,5)개 이하의 장비에서 (1개 혹은 여러)능력치를 선택해서 그대로 가져왔다고 할 수 있다.
선택되는 능력의 조합 31가지에 대해 능력치의 최대 합을 구해놓고
이들 조합 중 선택한 능력이 겹치지 않도록 min(k,5)개를 선택해서 최대 합을 찾는다.


#include<cstdio>
#include<algorithm>
using namespace std;
int t, n, c, m[32], a[5];
int f(int h, int l) {
    if (!l) return 0;
    int rt = 0;
    for (int i = 1; i < 32; i++) if (!(h&i)) rt = max(rt, f(h | i, l - 1) + m[i]);
    return rt;
}
int main() {
    for (scanf("%d", &t); t--;) {
        scanf("%d%d", &n, &c);
        fill(&m[0], &m[32], 0);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < 5; j++) scanf("%d", a + j);
            for (int j = 1; j < 32; j++) {
                int s = 0;
                for (int k = 0; k < 5; k++) if (1 << k&j) s += a[k];
                if (m[j] < s) m[j] = s;
            }
        }
        printf("%d\n", f(0, min(c, 5)));
    }
    return 0;
}

5520번: The Clocks

https://www.acmicpc.net/problem/5520


$O(n*M*4^n)$ // n: 스위치 개수, M: 영향을 주는 시계의 최대 개수

모든 경우를 조사한다.


#include<cstdio>
const int f[][5] = { { 0,1,3,4 },{ 0,1,2 },{ 1,2,4,5 },{ 0,3,6 },{ 1,3,4,5,7 },{ 2,5,8 },{ 3,4,6,7 },{ 6,7,8 },{ 4,5,7,8 } }, c[] = { 4,3,4,3,5,3,4,3,4 };
int g[9], r = 1e9, p;
int main() {
    for (int i = 0; i < 9; i++) scanf("%d", g + i);
    for (int i = 1 << 18; i--;) {
        int t[9] = {}, s = 0, j;
        for (j = 0; j < 9; j++)
            for (int k = 0; k < c[j]; k++) t[f[j][k]] = (t[f[j][k]] + (i >> j * 2) * 3) % 4, s += (i >> j * 2) % 4;
        for (j = 0; j < 9; j++) if (g[j] ^ t[j]) break;
        if (j == 9 && r > s) r = s, p = i;
    }
    for (int i = 0; i < 9; i++) for (int k = (p >> i * 2) % 4; k--;) printf("%d ", i + 1);
    return 0;
}

1182번: 부분집합의 합

https://www.acmicpc.net/problem/1182


$O(2^n)$

모든 경우를 조사한다.


#include<cstdio>
int n, s, r, a[20];
int main() {
    scanf("%d %d", &n, &s);
    for (int i = 0; i < n; i++) scanf("%d", a + i);
    for (int i = 1; i < 1 << n; i++) {
        int t = 0;
        for (int j = 0; j < n; j++) if (i & 1 << j) t += a[j];
        r += s == t;
    }
    printf("%d", r);
    return 0;
}

2098번: 외판원 순회

https://www.acmicpc.net/problem/2098


#include<cstdio>
#include<algorithm>
using namespace std;
int n, dp[1 << 16][16], w[16][16];
int f(int x, int y) {
    if (x == (1 << n) - 1) return w[y][0] ? w[y][0] : 1e9;
    if (!dp[x][y]) {
        dp[x][y] = 1e9;
        for (int i = 0; i<n; i++) if (w[y][i] && !(x & 1 << i))
            dp[x][y] = min(dp[x][y], f(x | 1 << i, i) + w[y][i]);
    }
    return dp[x][y];
}
int main() {
    scanf("%d", &n);
    for (int i = 0; i<n; i++) for (int j = 0; j<n; j++) scanf("%d", &w[i][j]);
    printf("%d", f(1, 0));
    return 0;
}