Benelux Algorithm Programming Contest 2019 赛后回顾
D. Problem D. Fractionstellar
Your parents decided that it would be “fun” to spend the entire Sunday walking near the Mookerheide close to Nijmegen.
Although you can pass the time by solving programming problems in your head, your siblings do not have the same luxury. After a short while, your younger sister Alice and your big brother Bob find themselves hopelessly bored. Together, they try to figure out if they can pass the time with a game (a problem that would later be referred to as the Bob and Alice Pastime Conundrum). Finally, they come up with the following simple game.
They find a single branch of length n that will be the main object of the game. Alternatingly, Alice and Bob choose a piece of branch and break it into two parts, in such a way that both parts have integer lengths. The last player who is able to break one of the pieces wins. Alice gets to start, as she is the younger of the two.
Of course, you already have the game figured out in your head. Assuming Bob plays optimally, can Alice win the game? And if so, what move should she make first?
input
A line containing a single integer 2 \le n \le 10^92≤n≤109, the length of the branch.
output
-
On the first line print the name of the person who wins, Alice or Bob.
-
If Alice can win, print the length of a piece of branch Alice can break off as a winning move. This should be an integer between 1 and n−1n−1, inclusive.
If there are multiple valid solutions, you may output any one of them.
题意:
给定长度为N的木条,A、B两人轮流切割,要求每次切割后剩下的两段的长度均为整数,最后一次切割的人为胜者。A先手,在双方都执行最优策略的情况下最后谁会获胜?
题解:
获胜与否只与木条可以被切割的次数有关,且木条可以被切割的次数与策略无关,只与自身长度有关。
木条长度为奇数,则可以被切割偶数次,此时后手者获胜;长度为偶数,则可以被切割奇数次,此时先手者获胜。
D. Problem D. Fractionstellar
You have recently acquired a new job at the Bank for Acquiring Peculiar Currencies. Here people can make payments, and deposit or withdraw money in all kinds of strange currencies. At your first day on the job you help a customer from Nijmegia, a small insignificant country famous for its enormous coins with values equal to powers of 10, that is, 1, 10, 100, 1000, etc. This customer wants to make a rather large payment, and you are not looking forward to the prospect of carrying all those coins to and from the vault.
You therefore decide to think things over first. You have an enormous supply of Nijmegian coins in reserve, as does the customer (most citizens from Nijmegia are extremely strong). You now want to minimize the total number of coins that are exchanged, in either direction, to make the exact payment the customer has to make.
For example, if the customer wants to pay 83 coins there are many ways to make the exchange. Here are three possibilities:
-
Option 1. The customer pays 8 coins of value 10, and 3 coins of value 1. This requires exchanging 8 + 3 = 11 coins.
-
Option 2. The customer pays a coin of value 100, and you return a coin of value 10, and 7 coins of value 1. This requires exchanging 1 + 1 + 7 = 9 coins.
-
Option 3. The customer pays a coin of value 100, and 3 coins of value 1. You return 2 coins of value 10. This requires exchanging 1 + 3 + 2 = 6 coins.
It turns out the last way of doing it requires the least coins possible.
input
A single integer the amount the customer from Nijmegia has to pay.
output
Output the minimum number of coins that have to be exchanged to make the required payment.
题意:
存在面值为1、10、100、1000……10^n……的金币。现在A、B两人交易,A给B一些金币,B给A一些金币,最终等价于A一共给了B N元。问A、B之间最少的金币交换数是多少?
即假设A打算给B 18元钱,那么A可以先给B2个10元,然后B给A两个1元,这样A、B之间一共交换了4枚金币,且这是最少的金币交易数。
题解:
记N一共有M位,
arr[i]:N的第i位的大小。注:arr[1]为N的最高位。
dp[i]:完成arr[] i~M位交易所需要的最少金币数。例如N=1234,那么dp[3]表示完成交易额为34的交易所需要的最少金币数。
显然dp[1]就是我们所需要的答案。
现在考虑dp[i]怎么由其他状态转移过来。为了完成第i位所需要的金额arr[i]*,我们有三种选择:
1、 给arr[i]枚面值为的金币。例如要3.XXX万,先给3万。
2、 给arr[i]+1枚面值为的金币. 例如要3.XXX万,给4万。
3、 给1枚面值为的金币. 例如要3.XXX万,给10万。
第一种策略下我们所需要的最少金币数 tmp=arr[i]+dp[i+1]
第二种策略下,我们多给一枚面值为的金币,此对方需要给我们价值为-arr[i+1~M]的金币,即我们所需要的最少金币数是arr[i]+1+cnt, cnt为交易额等于-arr[i+1~M]所需要的最少金币数。那么cnt该怎么求呢?首先对-arr[i+1~M]换一种表示方式。
定义:
brr[i+1]=9-arr[i+1],……,brr[M]=10-arr[M],则brr[i+1~M]=-arr[i+1~M];显然arr[1~M]+brr[1~M]=10^(M+1)。arr brr每一位的和都为10,需要进位。
(但是,如果arr[M]为0,即arr的最后一位为0的情况下,brr[M]=10。在这种情况下我们先预处理,减小M的值,直至arr[M]不为0在开始算法。因为交易额为XXXXX0 时的答案和交易额为XXXXX时答案相同。所以一开始给定的N的末尾有0,则可以先抹去0。其实这种情况不考虑也没啥问题。。。。)
类比于arr[]和dp[]之间的关系,我们定义gp[i]: 完成brr[] i~M位交易所需要的最少金币数。
回到策略二上:cnt=gp[i+1];所以情况二所需要的最少金币数 tmp=arr[i]+1+gp[i+1]。
策略三:直接给一枚的金币,arr[i~M]全部取补,所需的最少金币数 tmp=1+gp[i]。
综合以上三种情况,dp[i]=min{arr[i]+dp[i+1]; arr[i]+1+gp[i+1]; 1+gp[i] }
现在还有一个简单的问题:gp[]怎么求?显然gp[]的求法和dp[]一样。
gp[i]=min{brr[i]+gp[i+1]; brr[i]+1+dp[i+1]; 1+dp[i] }
现在又产生了一个问题,dp[i]和gp[i]的求解属于并行关系,但求dp[i]需要gp[i],在求gp[i]需要dp[i],即形成了环,这该怎么解决呢?
观察
dp[i]=min{arr[i]+dp[i+1]; arr[i]+1+gp[i+1]; 1+gp[i] }
gp[i]=min{brr[i]+gp[i+1]; brr[i]+1+dp[i+1]; 1+dp[i] }
发现对dp[i]的求解来说,若gp[i]取值为第三种测策,那么dp[i]不可能取第三种策略,所以dp[i]的第三种策略只需要考虑gp[i]的前两种策略;
即dp[i]=min{arr[i]+dp[i+1]; arr[i]+1+gp[i+1]; 1+(brr[i]+gp[i+1]);1+(brr[i]+1+dp[i+1]) }
同理,gp[i]的第三种策略也只需要考虑dp[i]的前两种策略,;
即gp[i]=min{brr[i]+gp[i+1]; brr[i]+1+dp[i+1]; 1+(arr[i]+dp[i+1]);1+(arr[i]+1+gp[i+1]) }。
最后就是初始值dp[M]和gp[M]的确定了,这个仔细想一下就明白了。
Dp[M]=min(arr[m],1+10-arr[m]),同理gp[M]=min(brr[m],1+10-brr[m]);
dp[]转移公式进一步化简,dp[i]=min{arr[i]+dp[i+1]; arr[i]+1+gp[i+1]; 1+brr[i]+gp[i+1] }这个公式也是可以的,但是忘了怎么化简的....
#include<cstdio> #include<cstring> #include<algorithm> #include <set> #include <vector> #include <iostream> #include <cmath> using namespace std; const int MAXN=1e3+10; int num[MAXN]; int pum[MAXN]; int dp[MAXN];//正位数 int gp[MAXN];//反数 int main() { char c; int index = 1; while (cin >> c) { if(c<'0'||c>'9')break; num[index++] = c - '0'; } num[index] = 0; while (num[index] == 0) index--; pum[index] = 10 - num[index]; for (int i = index - 1; i >= 1; i--) { pum[i] = 9 - num[i]; } dp[index] = min(num[index], 1 + 10 - num[index]); gp[index] = min(pum[index], 1 + 10 - pum[index]); for (int i = index - 1; i >= 1; i--) { dp[i]=min(num[i]+dp[i+1],num[i]+1+gp[i+1]); dp[i]=min(dp[i],1+9-num[i]+gp[i+1]); gp[i]=min(pum[i]+gp[i+1],pum[i]+1+dp[i+1]); gp[i]=min(gp[i],1+9-pum[i]+dp[i+1]); } cout << dp[1] << endl; return 0; }