JMK no matter what

SRM444 연습

250 삽질, 500 레코드 찍고 11등 했던 셋. 한번 돌아보았다. 근데 SRM444 가 꽤나 옛날이더라.. 미혼 시절이니... -_-;;;; 근데 250은 더 빨리 풀었지만 500은 완전 느리게 풀고, 1000 도 섭밋만 했지 틀리고.. 완전 패망이라능.. ㅠㅠ 점점 퇴화하는 것 같아. 1000 은 inclusion-exclusion principle 의 좋은 예. 그거 생각 못해서 일단 틀리고, 그거 코딩한 다음엔 off-by-one error 로 틀림.

250 UnfoldingTriangles

구현만 잘 하면 되는 문제.

lang:cpp
#include<iostream>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<string>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<fstream>
#include<cassert>
#include<numeric>
#include<set>
#include<map>
#include<queue>
#include<list>
#include<deque>
using namespace std;

#define FOR(i,a,b) for(int i = (a); i < (b); ++i)
#define REP(i,n) FOR(i,0,n)
#define FORE(it,x) for(typeof(x.begin()) it=x.begin();it!=x.end();++it)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define CLEAR(x,with) memset(x,with,sizeof(x))
#define sz size()
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef long long ll;


struct UnfoldingTriangles
{
    vector<string> g;
    int solve(int y, int x, int limit)
    {
        int ret = -1;
        for(int size = 1; ; ++size)
        {
            if(y-size+1 < 0 || x-size+1 < 0) break;
            if(x+1 < g[0].sz && g[y-size+1][x+1] == '#') break;
            if(y+1 < g.sz && g[y+1][x-size+1] == '#') break;
            bool seenBlack = false, seenWhite = false;
            int unfold = 0;

            for(int i = 0; i < size; ++i)
            {
                char ch = g[y - size + 1 + i][x - i];
                if(ch == '#') seenBlack = true;
                else if(ch == '.') seenWhite = true;
                else ++unfold;
            }
            if(seenWhite) break;
            if(!seenBlack)
                ret = ((size + 1) * size) / 2;
            limit -= unfold;
            if(limit < 0) break;
        }
        return ret;
    }
    int solve( vector <string> grid, int unfoldLimit )
    {
        g = grid;
        int ret = -1;
        REP(i,g.sz) REP(j,g[0].sz) ret = max(solve(i, j, unfoldLimit), ret);
        return ret;
    }
};

500 FourBlocks

Trivial exp time 동적 계획법. 4개짜리 블럭을 놓을 곳을 과거에는 2^h 를 모두 계산하면서 찾았는데, 이번에는 놓을 수 있는 후보 위치의 서브셋으로 한정해서 풀었다. 덕분에 런타임은 빠르지만 코딩타임은 시궁창. 아아.. ㅠ

lang:cpp
#include<iostream>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<string>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<fstream>
#include<cassert>
#include<numeric>
#include<set>
#include<map>
#include<queue>
#include<list>
#include<deque>
using namespace std;

#define FOR(i,a,b) for(int i = (a); i < (b); ++i)
#define REP(i,n) FOR(i,0,n)
#define FORE(it,x) for(typeof(x.begin()) it=x.begin();it!=x.end();++it)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define CLEAR(x,with) memset(x,with,sizeof(x))
#define sz size()
typedef vector<int> VI;
typedef vector<VI> VVI;
typedef long long ll;

int cache[1<<10][26];
struct FourBlocks
{
    int h, w, taken[26];
    int go(int x, int prevCol)
    {
        if(x == w) return h - __builtin_popcount(prevCol);
        int& ret = cache[prevCol][x];
        if(ret != -1) return ret;
        ret = go(x+1, taken[x]) + (h - __builtin_popcount(prevCol));
        if(x)
        {
            int canPut = (1 << (h-1)) - 1;
            canPut &= ~(prevCol | (prevCol >> 1));
            canPut &= ~(taken[x] | (taken[x] >> 1));
//            int canPut = ~(prevCol | taken[x]) & ((1 << (h-1))-1);
            for(int fours = canPut; fours; fours = ((fours-1) & canPut))
            {
                if(fours & (fours << 1)) continue;
                int cand = __builtin_popcount(fours) * 16;
                int occupy = fours | (fours << 1);
                assert(!(occupy & prevCol));
                assert(!(occupy & taken[x]));
                cand += h - __builtin_popcount(prevCol | occupy);
                cand += go(x+1, taken[x] | occupy);
                ret = max(ret, cand);
            }
        }
        return ret;
    }
    int maxScore( vector <string> grid )
    {
        h = grid.sz; w = grid[0].sz;
        CLEAR(taken,0);
        REP(i,h) REP(j,w) if(grid[i][j] == '1') taken[j] += (1<<i);
        CLEAR(cache,-1);
        int add = 0;
        REP(i,w) add += __builtin_popcount(taken[i]);
        return go(0, (1<<h)-1) + add;
    }
};

1000 AvoidFour

처음에는, 대회했을 때랑 똑같은 패턴으로, 44, 444, 4444.. 만 고려했다가 답이 안나오는 사태에 봉착. 다행히도, 이번에는 44 를 한개의 블록으로 해서 44^n 을 모두 찾으면 되겠다 라는 생각을 할 수 있었다. 그러나, 44 와 444 의 LCM 들은 두번 빼진다는 걸 생각 못했지롱. 후.....

처음에는 그리고 옛날 ValidPlates 인가 하는 문제에서 봤던 starting symbol 패턴을 써서 풀었는데, 생각해보면 A\^1 + A\^2 + ... A\^n 을 계산해서 푸는 것이 훨씬 말끔하다. 그리고 4 외의 숫자들은 한 개의 스테이트로 합칠 수 있다는 것을 이용하면 이와 같이 깔끔한 코드를 얻을 수 있다.. ㅠ

lang:cpp
#include<iostream>
#include<cstring>
#include<algorithm>
#include<sstream>
#include<string>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<fstream>
#include<cassert>
#include<numeric>
#include<set>
#include<map>
#include<queue>
#include<list>
#include<deque>
using namespace std;

#define FOR(i,a,b) for(int i = (a); i < (b); ++i)
#define REP(i,n) FOR(i,0,n)
#define FORE(it,x) for(typeof(x.begin()) it=x.begin();it!=x.end();++it)
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define CLEAR(x,with) memset(x,with,sizeof(x))
#define sz size()
typedef long long ll;
typedef vector<ll> VI;
typedef vector<VI> VVI;
const ll M = 1000000007;

VVI operator + (const VVI& a, const VVI& b)
{
    int n = a.sz;
    VVI ret(n, VI(n, 0));
    REP(i,n) REP(j,n) ret[i][j] = (a[i][j] + b[i][j]) % M;
    return ret;
}
VVI operator * (const VVI& a, const VVI& b)
{
    int n = a.sz;
    VVI ret(n, VI(n, 0));
    REP(i,n) REP(j,n) REP(k,n) (ret[i][j] += ((a[i][k] * b[k][j]) % M)) %= M;
    return ret;
}

VVI pow(const VVI& unit, ll N)
{
    assert(N);
    if(N == 1) return unit;
    if(N % 2) return pow(unit, N-1) * unit;
    VVI half = pow(unit, N/2);
    return half*half;
}

// unit + unit**2 + .. + unit**N
VVI powsum(const VVI& unit, ll N)
{
    assert(N);
    if(N == 1) return unit;
    if(N % 2) return pow(unit, N) + powsum(unit, N-1);
    VVI half = powsum(unit, N/2);
    return half + (half * pow(unit, N/2));
}

struct AvoidFour
{
    int count( long long n)
    {
        VVI C(5, VI(5, 0));
        C[1][0] = 8; C[2][0] = 1;
        C[1][1] = 9; C[2][1] = 1;
        C[3][2] = 1; C[1][2] = 9;
        C[4][3] = 1; C[1][3] = 9;
        C[1][4] = 9;
        VI lens;
        for(ll unitLen = 44; unitLen <= n; unitLen = (unitLen * 10) + 4)
            lens.push_back(unitLen);

        ll A = 0;
        REP(mask,(1<<lens.sz))
        {
            ll lcm = 1;
            REP(i,lens.sz) if(mask&(1<<i))
            {
                lcm = (lcm / __gcd(lcm, lens[i])) * lens[i];
                if(lcm > n) break;
            }
            if(lcm > n) continue;
            VVI un = powsum(pow(C, lcm), n / lcm);
            ll D = 0;
            REP(j,5) (D += un[j][0]) %= M;
            if(__builtin_popcount(mask) % 2 == 0)
                A = (A + D) % M;
            else
                A = (A + M - D) % M;
        }
        return A;
    }
};
2009-11-16 10:01:21 | JM | /logs/topcoder/ | 0 Comments

Leave a comment