takapt0226's diary

競技プログラミングのことを書きます

POJ 3273 Monthly Expense

解法

月の出費の上限で二分探索

ソースコード

int n, m;
int money[ten(5) + 100];
bool ok(int month_spend)
{
    int months = 1, spend = 0;
    rep(i, n)
    {
	if (spend + money[i] > month_spend)
	{
	    ++months;
	    spend = 0;
	}
	spend += money[i];
    }
    return months <= m;
}
int main()
{
    scanf("%d%d", &n, &m);
    rep(i, n)
	scanf("%d", money + i);

    int low = *max_element(money, money + n) - 1;
    int high = ten(9) + 100;
    while (high - low > 1)
    {
	int mid = (high + low) / 2;
	if (ok(mid))
	    high = mid;
	else
	    low = mid;
    }
    printf("%d\n", high);
}

POJ 3272 Cow Traffic

問題の意味がなかなかわからなかった

問題概要

ノード数nのDAGが与えられる。入力次数が0のノードからnへのパスを全て列挙する。各辺ごとにいくつのパスに含まれているかを数えたとき、最大のパス数はいくつか。

解法

辺i -> jを含むパス数は、(入力次数0のノード -> iのパス数) * (j -> nのパス数)で求められる
前者は元のグラフを逆辺にしたグラフ上でdp, 後者は元のグラフ上でdpして求める

ソースコード

int dfs(int v, const vector<vector<int> >& g, int* dp)
{
    if (dp[v] != -1)
	return dp[v];

    int res = 0;
    rep(i, g[v].size())
	res += dfs(g[v][i], g, dp);
    return dp[v] = res;
}
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    vector<vector<int> > g(n), rg(n);
    while (m--)
    {
	int a, b;
	scanf("%d%d", &a, &b);
	--a, --b;
	g[a].pb(b);
	rg[b].pb(a);
    }

    int dp[5252], rdp[5252];
    CL(dp, -1);
    CL(rdp, -1);

    dp[n - 1] = 1;
    rep(i, n - 1)
	if (rg[i].empty())
	    rdp[i] = 1;    // 0 indegree in original graph

    int res = -1;
    rep(i, n) rep(j, g[i].size())
    {
	int u = i, v = g[i][j];
	int paths = dfs(u, rg, rdp) * dfs(v, g, dp);
	chmax(res, paths);
    }
    printf("%d\n", res);
}

POJ 3280 Cheapest Palindrome

解法

区間dp
dp[i][j] = [i, j)をpalindromeにする最小コスト

ソースコード

int main()
{
    int m, n;
    string s;
    cin >> n >> m >> s;

    const int inf = ten(8);
    int cost[256];
    fill(cost, cost + 256, inf);
    while (n--)
    {
	char c;
	int add, del;
	cin >> c >> add >> del;
	cost[c] = min(add, del);
    }

    static int dp[2048][2048]; // [i, j)
    erep(i, m) erep(j, m)
	dp[i][j] = inf;

    rep(i, m)
    	dp[i][i] = dp[i][i + 1] = 0;

    rep(len, m)
    {
	for (int i = 0, j = len; j <= m; ++i, ++j)
	{
	    if (i > 0)
		chmin(dp[i - 1][j], dp[i][j] + cost[s[i - 1]]);
	    if (j < m)
		chmin(dp[i][j + 1], dp[i][j] + cost[s[j]]);
	    if (i > 0 && j < m && s[i - 1] == s[j])
		chmin(dp[i - 1][j + 1], dp[i][j]);		
	}
    }
    printf("%d\n", dp[0][m]);
}

POJ 3279 Fliptile

解法

一番上の行のflipの仕方を決めると、flipの仕方は一意に決まるので、一番上の行のflipパターンを全て試す。
蟻本に載ってる問題です

ソースコード

int h, w, tile[22][22];
int work[22][22],  pos[22][22];
int f;
void flip(int x, int y)
{
    work[y][x] ^= 1;
    pos[y][x] = 1;
    rep(i, 4)
    {
	int tx = x + dx[i], ty = y + dy[i];
	if (valid(tx, ty, w, h))
	    work[ty][tx] ^= 1;
    }
}
int main()
{
    scanf("%d%d", &h, &w);
    rep(i, h) rep(j, w)
	scanf("%d", &tile[i][j]);

    int min_flip = 33333, res_pos[22][22];
    rep(s, bin(w))
    {
	CL(pos, 0);
	COPY(work, tile);
	f = 0;

	rep(x, w)
	    if (s >> x & 1)
		flip(x, 0);

	for (int y = 1; y < h; ++y)
	    rep(x, w)
		if (work[y - 1][x])
		    flip(x, y);

	bool ok = true;
	rep(y, h) rep(x, w)
	    ok &= !work[y][x];


	if (ok && f < min_flip)
	{
	    min_flip = f;
	    COPY(res_pos, pos);
	}
    }
    if (min_flip == 33333)
	puts("IMPOSSIBLE");
    else
    {
	rep(y, h) rep(x, w)
	    printf("%d%c", res_pos[y][x], x + 1 == w ? '\n' : ' ');
    }
}

POJ 3277 City Horizon

解法

まずは、座標圧縮する
ビルの開始点、終了点に高さをメモする
座標の小さい点から走査していき、ビルの開始点ではそのビルの高さを集合に加え、終了点では集合から削除する
高さの集合の管理には追加、削除が高速にできるものを使うと良い
追加、削除がO(logn)のデータ構造を使うと、計算量はO(nlogn)

multisetを使うと実装が楽になるが、TLEしたのでpriority_queue + カウンタ(num)で書き直した

ソースコード

int main()
{
    const int M = 4 * ten(4);
    int n;
    int a[M], b[M], h[M]; // [a, b)
    scanf("%d", &n);
    rep(i, n)
	scanf("%d%d%d", a + i, b + i, h + i);

    vector<int> pos;
    rep(i, n)
	pos.pb(a[i]), pos.pb(b[i]);
    uniq(pos);

    vector<int> uh(h, h + n);
    uniq(uh);

    static vector<int> start[2 * M], end[2 * M];
    rep(i, n)
    {
	int s = lower_bound(all(pos), a[i]) - pos.begin();
	int e = lower_bound(all(pos), b[i]) - pos.begin();
	int hi = lower_bound(all(uh), h[i]) - uh.begin();
	start[s].pb(hi);
	end[e].pb(hi);
    }

    int num[M] = {};
    priority_queue<pint> h_set;	// (height, hi)
    ll area = 0;
    rep(pi, pos.size())
    {
	rep(i, start[pi].size())
	    if (num[start[pi][i]]++ == 0)
		h_set.push(pint(uh[start[pi][i]], start[pi][i]));
	rep(i, end[pi].size())
	    --num[end[pi][i]];

	while (!h_set.empty() && num[h_set.top().second] == 0)
	    h_set.pop();

	if (!h_set.empty())
	{
	    ll max_h = h_set.top().first;
	    ll len = pos[pi + 1] - pos[pi];
	    area += len * max_h;
	}
    }
    printf("%lld\n", area);
}

POJ 3278 Catch That Cow

解法

bfs

ソースコード

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);

    const int inf = ten(7);
    int d[2 * ten(5) + 100];
    fill(d, d + 2 * ten(5), inf);
    queue<int> q;
    d[n] = 0;
    q.push(n);
    while (!q.empty())
    {
	int p = q.front();
	q.pop();
	int nd = d[p] + 1;
	int to[] = { p - 1, p + 1, 2 * p };
	rep(i, 3)
	{
	    if (to[i] >= 0 && to[i] < ten(5) + 10)
	    {
		if (nd < d[to[i]])
		{
		    d[to[i]] = nd;
		    q.push(to[i]);
		}
	    }
	}
    }
    printf("%d\n", d[k]);
}

POJ 3629 Card Stacking

ソースコード

int main()
{
    int n, k, p;
    scanf("%d%d%d", &n, &k, &p);
    int m = k / n;
    vector<int> res;
    queue<int> deck;
    rep(i, k)
	deck.push(i + 1);

    for (int i = 0; !deck.empty(); ++i %= n)
    {
	if (i == n - 1)
	    res.pb(deck.front());
	deck.pop();
	rep(j, p)
	{
	    deck.push(deck.front());
	    deck.pop();
	}
    }
    sort(all(res));
    rep(i, m)
	printf("%d\n", res[i]);
}