2020年3月24日360筆試題目


  做了360的筆試,感覺真的是運氣很好了,這次的筆試真的算非常簡單的了。廢話不多說,看題目。

題目一

題目大概是,有一段DNA,包含兩種字符,A和T,科學家可以通過兩種方式修改這種DNA,第一種是替換DNA上兩個核酸的位置。第二種是直接把字符修改爲另一種字符
輸入兩行,第一行是原始的DNA,第二行是目標DNA,保證長度是相同的
求最少的修改次數

樣例輸入:
ATTTAA
TTAATT

輸出:3

  這個題目看起來好像有點複雜,仔細一想其實很簡單,就兩個字符,不一樣的時候可以交換,也可以更改。顯然長度是一致的,沒有改變長度的操作,所以很直接,如果可以交換直接交換,如果不能交換則修改。交換減少的不一樣字符的數目爲2,修改減少不一致的數目爲1。
  再來看交換,如果把A換成T,肯定有一個T被換成了A,如果把已經配對的T換成了A,那麼相當於沒換,因爲不一樣的數目沒變。所以肯定是兩者之間的互換。但是什麼時候就不能交換了呢,顯然就是所有的T已經被A交換完了,或者A被T交換完了。
  如果已經有一種字符被交換完了,那麼剩下的字符就只能修改了。所以統計不一樣的字符數目,分開統計A不一樣的時候,和T不一樣的時候。交換的數目顯然就是兩個數中較小的數目。更改的數目就是多的數交換之後剩下的數目。所以總的修改次數,就是隻管多的那個不一樣的字符。
  上面的例子中,兩個DNA不相同的字符的索引有0,2,3,4,5。與原串的A不一樣的字符索引是0,4,5。與原串的T不一樣的字符索引是2,3。顯然把0,4和2,3交換之後,剩下的5進行替換即可。所以就是統計與原串中A和T不一樣的字符的數目,兩者多的就是總的修改次數。

python 代碼

origin=input()
target=input()
ori={'A':0,'T':0}
for o,t in zip(origin,target):
    if o!=t:
        ori[o]+=1
print(max(ori['A'],ori['T']))

C++代碼

int main()
{
	string origin, target;
	cin >> origin;
	cin >> target;
	int a[2] = { 0,0 };
	for (int i = 0; i < origin.size(); i++)
		if (origin[i] != target[i])
			a[origin[i] == 'A'] += 1;
	cout << (a[0]>a[1]? a[0]:a[1]);
	return 0;
}

  這個問題,有人比較疑惑爲什麼自己不是使用max,也能正確提交,我分析之後,發現可能是大家使用這個公式(A+T+abs(AT))//2=max(A,T)\left( A+T+abs(A-T)\right)//2=max(A,T),具體是怎麼計算的,大家看公式自己理解。

第二題

有一個抽獎箱,n張中獎券,m張不中獎券,A和B輪流抽獎,如果抽中獎券就結束。但是B每抽一次後,沒抽中的話會把抽獎箱中的一張票給扔掉。如果所有的獎票都抽光了,則B獲勝。A先抽,求A獲勝的概率,保留4位小數
輸入: n和m輸出A獲勝的概率

樣例輸入:
2 3
輸出 0.6000

  對於輸出的0.5000的解釋是,A第一次抽中的概率是2/5=0.4,第二次抽中的概率是A和B都沒有抽中,概率是3/5 * 2/4這個時候剩下2張中獎券和一張不中獎券需要丟掉一張,有兩種情況,丟掉一張中獎券,概率是2/3,則A下次獲勝的概率是1/2,如果丟掉不中獎券,A下次獲勝的概率是1。這個事件發生的概率等於 3/5 * 2/4 * (2/3 * 1/2 + 1/3 *1)。然後兩次之和就是0.6000。
  一回合,最多用掉三張獎券,肯定有兩張是不中獎券,還有一張是丟的,可以是中獎券也可以是不中獎券。我們用掉三張之後,相當於問題分成了兩種情況,n-1,m-2和n,m-3這兩個子問題。而且子問題和原來的問題具有一致性,所以比較容易的寫出遞歸代碼。但是同樣的問題,會出現重複計算,那麼可能會想到給遞歸加備忘錄,這就是整個算法的思路。
  能否考慮自底向上的動態規劃,當然可以,但是沒有必要,因爲處理的情況比較多。還是遞歸的代碼處理起來簡潔。而且n和m,有很多子問題是不需要計算的。如果自底向上,不好判斷哪些需要計算,哪些不需要。所以選擇自頂向下的計算更好一些。
  處理返回情況了,當m<2的時候,因爲不夠一回合減兩張不中獎券了,就已經可以返回了,同理n<1也可以返回了。所有的情況,因爲沒有下一次,所以a必須獲勝,直接返回n/(n+m)。但是當m=2的時候,n=1,這個時候A也沒有下次了,獲勝的概率是n/(n+m)。
  如果n還很多,但是m只有兩張了,這個時候,顯然a沒抽中的話,只有m也沒抽中才可以獲勝,這個時候
A獲勝的概率就是抽中,n/(n+2)加上A沒抽中,B也沒抽中的概率,就是1/((m+n)(m+n1))1/((m+n)*(m+n-1))。兩者之和。
  其實這裏還可以用丟掉的獎券不影響A下次獲勝的概率這一條規則簡化一下代碼,但是我筆試的時候沒有考慮那麼多。

python代碼

n,m=[int(i) for i in input().split(' ')]
dp=[[0]*(m+1) for i in range(n+1)]
def func(n, m):
    summ=m+n
    if dp[n][m]:return dp[n][m]
    elif m<2 or n<1 or (m==2 and n==1):
        dp[n][m]=n/summ
    elif m==2:
        dp[n][m]=n/summ+2/(summ*summ-summ)
    else:
        cb=func(n, m - 3)*(m-2)/(summ-2) # 丟掉不中獎券後A獲勝的概率
        cr=func(n - 1, m - 2)*n/(summ-2) # 丟掉中獎券後A獲勝的概率
        dp[n][m]= n/summ+(cb+cr)*(m*m-m)/(summ*summ-summ) # A這次就獲勝的概率+A之和獲勝的概率
    return dp[n][m]

print('%.4f'%func(n,m))

C++代碼

double f(int n, int m, vector<vector<double>> probability)
{
	double sum = m + n;
	if (probability[n][m])return probability[n][m];
	if (m < 2 || n < 1 || (m == 2 && n == 1))probability[n][m] = n / sum;
	else {
		if(m==2)probability[n][m] = n / sum + 2 / (sum * sum - sum);
		else {
			double cb = f(n, m - 3, probability) * (m - 2) / (sum - 2); // 丟掉不中獎券後A獲勝的概率
			double cr = f(n - 1, m - 2, probability) * n / (sum - 2);  // 丟掉中獎券後A獲勝的概率
			probability[n][m] = n / sum + (cb + cr) * (m * m - m) / (sum * sum - sum);
		}
	}
	return probability[n][m];
}
int main()
{
	int n, m;
	cin >> n >> m;
	vector<vector<double>> probability(n + 1, vector<double>(m + 1, 0));
	cout << setiosflags(ios::fixed) << setprecision(4)<< f(n, m,probability);
}

  由於我自己提交試用的是python,C++代碼沒有經過在線提交,如果發現bug,歡迎指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章