バイトの競プロメモ

主に競技プログラミング

AtCoder Beginner Contest 099 - C - Strange Bank

C - Strange Bank

https://abc099.contest.atcoder.jp/tasks/abc099_c

 問題概要

 

Nが与えられる。以下の数を足してNを作るには、最小で何回の操作が必要か

  • 1 

  • 6 、62(=36) 、63(=216) 、

  • 9 、92(=81) 、93(=729) 、

また、同じ数字は何度でも使える

制約

  • 1≤N≤100000
  • Nは整数

自分の解法

使える数のリストを作り、動的計画法を使いました。

(Nが127の時、空白には-1が入っている)

  0 1 10 19 28 37 46 55 64 73 82 91 100 109 118 127
81                                                           0
36                                                           0
9                                                           0
6                                                           0
1                                                           0

Nから数字をどんどん引いていき、0にする事を目標に考えます。

表の数字は、操作をした回数を表しています。もし空白でなければ、その回数で列の数字が作れる。行の数を何回か使ってNを減らし、0にする最小の回数を求めたい。

 

空白でなければ、列の数は現状作れるので、そこから行(81とか)を引いた数も作れる。

右端から見ていき、もし同じ行で(上+左)列に数があれば、1足した数を入れる。

値が更新されたら、同列をその数で埋める。(↓は81について考えた時)

  0 1 10 19 28 37 46 55 64 73 82 91 100 109 118 127
81                        1                                   0
36                        1                                   0
9                        1                                   0
6                        1                                   0
1                        1                                   0

 

36について考えた場合。数字を使う度、数が増える

  0 1 10 19 28 37 46 55 64 73 82 91 100 109 118 127
81                        1                                   0
36        2    3            1    2                 1               0
9        2    3            1    2                1               0
6        2    3            1    2                1               0
1        2    3            1    2                1               0

 

数が衝突した場合、少ない方を採用する

  0 1 10 19 28 37 46 55 64 73 82 91 100 109 118 127
81                        1                                   0
36        2    3            1    2                1               0
9    3    2    3    3    2     1    2    4    3    2    1    3    2      1   0
6    3    2    3    3    2    1    2    4    3    2    1    3    2     1   0
1  4  3    2    3    3    2    1    2    4    3    2    1    3    2     1   0

 

上より127から数を引いていき、0を作る最小の操作回数は4だとわかります

Nが最大値のときでも、使える数字は17,8個程度なので計算量はO(10^7)となり間に合います。

import java.io.*;
import java.util.*;

public class Main
{
static int N;
static List<Integer> nlist;

public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
nlist = new ArrayList<>();
int i;
//使える数のリストを作り、ソート
int n9 = 1, n6 = 1;
for (i = 0; n9 * 9 <= N; i++)
{
n9 *= 9;
nlist.add(n9);
}
for (; n6 * 6 <= N; i++)
{
n6 *= 6;
nlist.add(n6);
}
nlist.add(1);
Collections.sort(nlist, Comparator.reverseOrder());

/////////////
int[][] dp = new int[nlist.size()][N + 1];
fill(dp, -1);
for (int j = 0; j < nlist.size(); j++)
dp[j][N] = 0;
for (i = 0; i < nlist.size(); i++)
{
int iva = nlist.get(i);
for (int ni = N - iva; ni >= 0; ni--)
{
if (dp[i][ni + iva] == -1) continue;
//niが作れる時 
if (dp[i][ni] == -1)
{
dp[i][ni] = dp[i][ni + iva] + 1;
}
else
{
dp[i][ni] = Math.min(dp[i][ni], dp[i][ni + iva] + 1);
}
//同列を更新された数で埋める
for (int j = i + 1; j < nlist.size(); j++)
{
dp[j][ni] = dp[i][ni];
}
}
}
int res = 1000000;
for (int j = 0; j < nlist.size(); j++)
{
if (dp[j][0] == -1) continue;
res = Math.min(res, dp[j][0]);
}

System.out.println(res);
}

static void fill(int[][] a, int v)
{
for (int i = 0; i < a.length; i++)
for (int j = 0; j < a[0].length; j++)
a[i][j] = v;
}
}

(追記)

【初級者競プロer向け】動的計画法(DP)を見て、改良しました

 

package com.company;

import java.io.*;
import java.util.*;

public class Main
{
static int N;
static List<Integer> nlist;
static int[] dp;

public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
nlist = new ArrayList<>();

//使える数のリストを作り、ソート
int n9 = 1, n6 = 1;
while (n9 * 9 <= N)
{
n9 *= 9;
nlist.add(n9);
}
while (n6 * 6 <= N)
{
n6 *= 6;
nlist.add(n6);
}
nlist.add(1);
Collections.sort(nlist);

//dp
dp = new int[N + 1];
for (int i = 0; i < N; i++)
{
for (int li = 0; li < nlist.size(); li++)
{
int v = nlist.get(li);
if (i + v > N) break;

if (dp[i + v] == 0) dp[i + v] = dp[i] + 1;
else dp[i + v] = Math.min(dp[i + v], dp[i] + 1);
}
}
System.out.println(dp[N]);
}
}

 

 いいねぇ