Google APAC test 2015 Round B Problem C - Card Game
2014-09-16 13:07
267 查看
源题地址:
https://code.google.com/codejam/contest/4214486/dashboard#s=p2
题意:
给定一个无序数组cards[]和数字k,如果存在连续的三个数a,b,c有如下关系:
c-b = b-a = k
那么就把这三个数删除,重复这个过程,直到不存在这样的关系,问怎样删才能使得最终剩下来的数的个数最少?最少为多少
分析:
使用区间dp方法, 建一个图有向图(准确的说是一个拓扑图),从图中找出一条最短路径
1)建图
图的节点表示每个字符,边(i,j)表示card[j] 是否可能成为card[i]的下一个数组(中间可能经过多次删除)。下图中以k=0为例子,
这样的i、j存在一个这样的关系 j = (i+1) + 3*D, 其中D表示其中删除的次数,为了缩减空间可以将图的空间申请如下:
bool dp[cards.size()/3+1][cards.size()+2]
为了便于递归,在card数组的最前面加入一个head表示起点(因为第一个数可能被删除),在最后面插入tail:
dp[i][j] = True 表示 cards[i*3+j+1]在经过多次删除后能够成为cards[j] 的下一个节点,那么递推关系是
dp[i+1][j] = dp[i+1][j] || ( dp[p][j] && dp[q][j+p*3+1] && dp[r][j+(p+q)*3+2) && dp[i-1-(p+q+r)] ) ( 0 <= p, q, r, (i-1-p-q-r) <= i-1)
上图中对应的dp数组生成如下:
实现的时候,需要五重循环,所以时间复杂度最高是O(N^5), 大数据最终2.5s运行完毕。
2)从图中找到最短的的路径head到tail的最短路径
也是用dp的思路,用length[i]表示保留card[i],从head出发到达card[i]的最短长度
length[i] = min(length[j]+1) (0 <= j < i, j是i所有可能的前驱)
最终的结果是head到tail的路径长度-1
代码:
def minCards(cards, k):
cards.insert(0, 0)
dp = [ [ False for i in range(0,len(cards)+1) ] for j in range(0, len(cards)/3+1) ]
for j in range(0, len(cards)+1):
dp[0][j] = True
for i in range(1, len(cards)/3+1):
for j in range(0, len(cards)+1):
if j + i*3 < len(cards):
for p in range(0, i):
if dp[i][j] == True:
break;
if dp[p][j]:
for q in range(0, i-p):
if dp[q][j+3*p+1]:
for r in range(0, i-(p+q)):
if dp[r][j+3*(p+q)+2] and dp[i-1-p-q-r][j+3*(p+q+r)+3] and cards[j+3*p+1]+k == cards[j+3*(p+q)+2] and cards[j+3*(p+q)+2]+k == cards[j+3*(p+q+r)+3]:
dp[i][j] = True
length = range(0, len(cards)+2)
for i in range(0, len(cards)+1):
for j in range(0, len(cards)/3+1):
if j*3+i < len(cards)+2:
if dp[j][i] and length[j*3+i+1] > length[i]+1:
length[j*3+i+1] = length[i]+1
return length[len(cards)]-1
fin = open('c_large.in','r')
fout = open('c_large.out','w')
line = fin.readline()
n = int(line)
for i in range(0,n):
line = fin.readline()
line = line.split(' ')
k = int(line[1])
line = fin.readline()
line = line.split(' ')
cards = [int(j) for j in line]
res = minCards(cards, k)
fout.write("Case #%d: %s\n"%(i+1, res))
fin.close()
fout.close()
https://code.google.com/codejam/contest/4214486/dashboard#s=p2
题意:
给定一个无序数组cards[]和数字k,如果存在连续的三个数a,b,c有如下关系:
c-b = b-a = k
那么就把这三个数删除,重复这个过程,直到不存在这样的关系,问怎样删才能使得最终剩下来的数的个数最少?最少为多少
分析:
使用区间dp方法, 建一个图有向图(准确的说是一个拓扑图),从图中找出一条最短路径
1)建图
图的节点表示每个字符,边(i,j)表示card[j] 是否可能成为card[i]的下一个数组(中间可能经过多次删除)。下图中以k=0为例子,
这样的i、j存在一个这样的关系 j = (i+1) + 3*D, 其中D表示其中删除的次数,为了缩减空间可以将图的空间申请如下:
bool dp[cards.size()/3+1][cards.size()+2]
为了便于递归,在card数组的最前面加入一个head表示起点(因为第一个数可能被删除),在最后面插入tail:
dp[i][j] = True 表示 cards[i*3+j+1]在经过多次删除后能够成为cards[j] 的下一个节点,那么递推关系是
dp[i+1][j] = dp[i+1][j] || ( dp[p][j] && dp[q][j+p*3+1] && dp[r][j+(p+q)*3+2) && dp[i-1-(p+q+r)] ) ( 0 <= p, q, r, (i-1-p-q-r) <= i-1)
上图中对应的dp数组生成如下:
实现的时候,需要五重循环,所以时间复杂度最高是O(N^5), 大数据最终2.5s运行完毕。
2)从图中找到最短的的路径head到tail的最短路径
也是用dp的思路,用length[i]表示保留card[i],从head出发到达card[i]的最短长度
length[i] = min(length[j]+1) (0 <= j < i, j是i所有可能的前驱)
最终的结果是head到tail的路径长度-1
代码:
def minCards(cards, k):
cards.insert(0, 0)
dp = [ [ False for i in range(0,len(cards)+1) ] for j in range(0, len(cards)/3+1) ]
for j in range(0, len(cards)+1):
dp[0][j] = True
for i in range(1, len(cards)/3+1):
for j in range(0, len(cards)+1):
if j + i*3 < len(cards):
for p in range(0, i):
if dp[i][j] == True:
break;
if dp[p][j]:
for q in range(0, i-p):
if dp[q][j+3*p+1]:
for r in range(0, i-(p+q)):
if dp[r][j+3*(p+q)+2] and dp[i-1-p-q-r][j+3*(p+q+r)+3] and cards[j+3*p+1]+k == cards[j+3*(p+q)+2] and cards[j+3*(p+q)+2]+k == cards[j+3*(p+q+r)+3]:
dp[i][j] = True
length = range(0, len(cards)+2)
for i in range(0, len(cards)+1):
for j in range(0, len(cards)/3+1):
if j*3+i < len(cards)+2:
if dp[j][i] and length[j*3+i+1] > length[i]+1:
length[j*3+i+1] = length[i]+1
return length[len(cards)]-1
fin = open('c_large.in','r')
fout = open('c_large.out','w')
line = fin.readline()
n = int(line)
for i in range(0,n):
line = fin.readline()
line = line.split(' ')
k = int(line[1])
line = fin.readline()
line = line.split(' ')
cards = [int(j) for j in line]
res = minCards(cards, k)
fout.write("Case #%d: %s\n"%(i+1, res))
fin.close()
fout.close()
相关文章推荐
- Google中国2015校园招聘笔试Round D APAC Test Problem C. Sort a scrambled itinerary
- Google APAC test 2015 Round B Problem A - Password Attacker
- Google APAC test 2015 Round B Problem B - New Years Eve
- Google APAC test 2015 Round B Problem D-Parentheses Order
- Google中国2015校园招聘笔试Round D APAC Test Problem A. Cube IV
- Google中国2015校园招聘笔试Round D APAC Test Problem B. GBus count
- Google中国2015校园招聘笔试Round D APAC Test Problem D. Itz Chess
- Problem A. Dynamic Grid Google APAC 2016 University Test Round D
- Problem C. Codejamon Cipher Google APAC 2017 University Test Round D
- Problem C. Partioning Number Google APAC 2017 University Test Round E
- Problem A. gRanks Google APAC 2016 University Test Round C
- [Practice Round APAC test 2016]Problem A. Bad Horse(Google 2016笔试题)
- Problem A. Travel Google APAC 2016 University Test Round B
- Problem C. Jane's Flower Shop Google APAC 2017 University Test Round A
- Problem C. Not So Random Google APAC 2017 University Test Practice Round
- Problem B. Sherlock and Watson Gym Secrets Google APAC 2017 University Test Round B
- Problem A. Googol String Google APAC 2016 University Test Round A
- Problem A. Lazy Spelling Bee Google APAC 2017 University Test Practice Round
- Problem B. gWheels Google APAC 2016 University Test Round B
- Problem B. Robot Rock Band Google APAC 2017 University Test Practice Round