1. 程式人生 > >2018-2019賽季多校聯合新生訓練賽第一場題解整和補題

2018-2019賽季多校聯合新生訓練賽第一場題解整和補題

做這個題用了沒多長時間就a了四道題,然後覺得我可以做出更多的時候突然發現別的都不太會了..到最後才拿到一個銅牌後來聽說這些
都是2011年慈溪的小學奧賽題,瞬間心態崩了...這都大學了做小學題都這麼費勁啊到頭來,看來還得更努力才行啊~!下面開始咯~

問題 A: 錄取分數線

題目描述

新學年,學校將成立資訊學興趣小組提高班。由於指導教師精力有限,只能以選拔考試的成績為依據,按從高到低的分數,從N個參加選拔的學生中錄取不超過M個成員。錄取的成員要儘可能地多,但不得超過M個(含M個)。由於可能會有並列分數出現,為了保證公平,有時只得忍痛割愛,可能錄取的成員會達不到計劃數M。請你程式設計劃定錄取分數線。

輸入

有N+1行,第一行是報名人數N和錄取人數M。以下N行是考試成績,已按從高到低的順序排列。N、M和成績均是1000以內的正整數,N≥M。資料保證不會所有的成績都相同。

輸出

只有1行,為錄取分數線。

樣例輸入
複製樣例資料 10 5
99
98
97
96
95
94
93
92
91
90

樣例輸出
95

這個題上來做的時候慌了,因為剛從廁所出來,很害怕我剛一點開就發現別的大佬都做了兩道題 其實很簡單 只需要
判斷一下m-1的前一項和m-1項是否相等 如果相等的話就不能輸出,程式碼如下:
#include<bits/stdc++.h>
using namespace
std; int a[2000]; int main() { int n,m; cin>>n>>m; for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) { if(a[m-1]==a[m]) m--;//如果相等的話輸出的時候 } cout<<a[m-1]; }

問題 E: 誰是冠軍
題目描述

小Q自從參加某小學計算機興趣小組以來,對程式設計產生了濃厚的興趣。他發現用計算機程式設計不但可以訓練思維,還可以解決學習和生活中的一些實際問題。比如,世界盃足球賽時,小Q就經常把其中的一些球隊列出來,組成一個小團隊,然後根據規則計算積分,並根據積分的高低看看這個團隊內誰是冠軍。假如某次足球賽的積分規則如下:每勝一局得3分,每平一局得1分,每輸一局扣1分,積分最高者為冠軍。小Q就想編這樣一個程式,輸入若干球隊的成績,就能自動求出這個團隊中誰是冠軍。你也能編一個嗎?

輸入

輸入有兩行,第一行是輸入的球隊數,第二行是每隊的比賽成績,依次為球隊編號、勝局數、平局數、負局數(均為小於1000的整數),每個資料間用一空格隔開。輸入的資料保證積分各不相同。

輸出

只有一個數,就是冠軍隊的編號。

樣例輸入
複製樣例資料 4
1 5 4 3
2 3 4 5
3 6 3 3
4 4 2 6

樣例輸出
3
這題的難度算是偏簡單 只需要開一個結構體,然後將和加起來排序,之後最大的輸出即可:

#include<bits/stdc++.h>
using namespace std;
struct node {
	int id;
	int b;
	int c;
	int d;
	int sum=0;
}e[2000];
bool cmp (node a,node b) {
	return a.sum>b.sum;
}
int main() {
	int n,f[2000];
	cin>>n;
	for(int i=0;i<n;i++) {
		cin>>e[i].id>>e[i].b>>e[i].c>>e[i].d;
		e[i].sum=e[i].b*3+e[i].c-e[i].d;
	}
	sort(e,e+n,cmp);
	cout<<e[0].id;
}

問題 F: 搭積木的訣竅題目描述

小Q的程式設計技術在一次搭積木比賽中也成了祕密武器。原來,比賽的規則是這樣的:給你N個小木塊(全部為一樣大小的正方體),快速搭成如下圖規則的形狀(下圖為5層的規模),要求層數為最大限度。由於小Q編了個程式,只要輸入小木塊個數N,就可以馬上求出最多可以搭幾層,還剩幾個,所以小Q每次都是一次成功,從不需要翻工,速度也就領先了。你會編小Q這樣的程式嗎?
在這裡插入圖片描述

輸入

只有一個整數N,表示小木塊的個數,已知1≤N≤30000。

輸出

有兩行整數,第一行是最多可以堆的層數,第二行是剩餘的小木塊數。

樣例輸入
複製樣例資料 37

樣例輸出
5
2
這個題可以用數學公式來解決 我有個大佬同學說忘記了數學公式,於是用模擬做的,我覺得太強了…
程式碼:
注意 到最後的時候如果多減了一次的話n會變成負數,這次要加上上次減去的那一層,因為c也多加了1,所以要及時減去。

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n,sum=0,c=0,a=1;
	cin>>n;
	while(n>=0) {
		n-=(a*(a+1))/2;
		a++;
		c++;
	}
	if(n<0) {
		n+=(a-1)*a/2;
		c--;
	}
	cout<<c<<"\n"<<n;
}

問題 G: 卡布列克常數
題目描述

最近,小Q在數學興趣課中瞭解了“卡布列克常數”。卡布列克是一位數學家,他在研究數字時發現:任意一個不是用完全相同數字組成的四位數,如果對它們的每位數字重新排序,組成一個最大的數和一個最小的數,然後用最大數減去最小數,差不夠四位數時補零,類推下去,最後將變成一個固定的數:6174,這就是卡布列克常數。
例如:4321-1234=3087
8730-378=8352
8532-2358=6174
7641-1467=6174
……
小Q想,我能不能程式設計來驗證呢?輸入一個符合條件的四位數,然後驗證運算過程。

輸入

共1行,為任意一個不是用完全相同數字組成的四位數。

輸出

變為卡布列克常數的運算過程,由若干行組成,每行是一個算式,不含空格。

樣例輸入
複製樣例資料 4321

樣例輸出
4321-1234=3087
8730-378=8352
8532-2358=6174

這題第一眼看上去我覺得用字串寫比較方便 但是寫著寫著發現每一次找最小和最大的數都很麻煩,而且
如果最後一位有0的話就非常的麻煩 ,於是最終決定定義兩個函式,找到最大值最小值,返回以後再讓他們
相減,等於答案的時候就退出迴圈。
程式碼:

#include<bits/stdc++.h>
using namespace std;
bool cmp(int a,int b) {
	return a>b;
}
int maxn(int x) {
	int a[4];
	for(int i=3;x>0;i--) {
		a[i]=x%10;
		x/=10;
	}
	sort(a,a+4,cmp);
	return a[0]*1000+a[1]*100+a[2]*10+a[3];
}
int minn(int x) {
	int a[4];
	for(int i=3;x>0;i--) {
		a[i]=x%10;
		x/=10;
	}
	sort(a,a+4);
	return a[0]*1000+a[1]*100+a[2]*10+a[3];
}
int main() {
	int n,sum;
	cin>>n;
	while(n!=6174) {
		cout<<maxn(n)<<"-"<<minn(n)<<"="<<maxn(n)-minn(n);
		n=maxn(n)-minn(n);
		cout<<endl;
	}
}

問題 D: 傳話遊戲
[提交] [狀態] [討論版] [命題人:admin]

題目描述

有這樣一個朋友網路,如果a認識b,那麼a收到某個訊息,就會把這個訊息傳給b,以及所有a認識的人。但是,請你注意,如果a認識b,b不一定認識a。現在我們把所有人從1到n編號,給出所有“認識”關係,問如果i釋出一條新訊息,那麼會不會經過若干次傳話後,這個訊息傳回給了i(1≤i≤n)。

輸入

第1行是兩個數n(n<1000)和m(m<10000),兩數之間有一個空格,表示人數和認識關係數。接下來的m行,每行兩個數a和b,表示a認識b(1≤a,b≤n)。認識關係可能會重複給出,但1行的兩個數不會相同。

輸出

一共有n行,每行一個字元T或F。第i行如果是T,表示i發出一條新訊息會傳回給i;如果是F,表示i發出一條新訊息不會傳回給i。

樣例輸入
4 6
1 2
2 3
4 1
3 1
1 3
2 3

樣例輸出
T
T
T
F
本題使用了floyd演算法,是求最短路的一種演算法,定義如下:

在電腦科學中,Floyd-Warshall演算法是一種在具有正或負邊緣權重(但沒有負週期)的加權圖中找到最短路徑的演算法。
演算法的單個執行將找到所有頂點對之間的最短路徑的長度(加權)。雖然它不返回路徑本身的細節,
但是可以通過對演算法的簡單修改來重建路徑。
該演算法的版本也可用於查詢關係R的傳遞閉包,或(與Schulze投票系統相關)在加權圖中所有頂點對之間的最寬路徑。
Floyd-Warshall演算法是動態規劃的一個例子,並在1962年由Robert Floyd以其當前公認的形式出版。
然而,它基本上與Bernard Roy在1959年先前發表的演算法和1962年的Stephen Warshall中找到圖形的傳遞閉包基本相同,
並且與Kleene的演算法密切相關 在1956年)用於將確定性有限自動機轉換為正則表示式。
演算法作為三個巢狀for迴圈的現代公式首先由Peter Ingerman在1962年描述。
該演算法也稱為Floyd演算法,Roy-Warshall演算法,Roy-Floyd演算法或WFI演算法。 [2] 

下面是核心思路通過一個圖的權值矩陣求出它的每兩點間的最短路徑矩陣。 [3] 
從圖的帶權鄰接矩陣A=[a(i,j)] n×n開始,遞迴地進行n次更新,即由矩陣D(0)=A,按一個公式,構造出矩陣D(1);
又用同樣地公式由D(1)構造出D(2);……;最後又用同樣的公式由D(n-1)構造出矩陣D(n)。
矩陣D(n)的i行j列元素便是i號頂點到j號頂點的最短路徑長度,稱D(n)為圖的距離矩陣,同時還可引入一個後繼節點矩陣path來記錄兩點間的最短路徑。
採用鬆弛技術(鬆弛操作),對在i和j之間的所有其他點進行一次鬆弛。所以時間複雜度為O(n^3);
狀態轉移方程
其狀態轉移方程如下: map[i,j]:=min{map[i,k]+map[k,j],map[i,j]};
map[i,j]表示i到j的最短距離,K是窮舉i,j的斷點,map[n,n]初值應該為0,或者按照題目意思來做。
當然,如果這條路沒有通的話,還必須特殊處理,比如沒有map[i,k]這條路。

演算法過程:
1,從任意一條單邊路徑開始。所有兩點之間的距離是邊的權,如果兩點之間沒有邊相連,則權為無窮大。
2,對於每一對頂點 u 和 v,看看是否存在一個頂點 w 使得從 u 到 w 再到 v 比已知的路徑更短。如果是更新它。
把圖用鄰接矩陣G表示出來,如果從Vi到Vj有路可達,則G[i][j]=d,d表示該路的長度;否則G[i][j]=無窮大。
定義一個矩陣D用來記錄所插入點的資訊,D[i][j]表示從Vi到Vj需要經過的點,初始化D[i][j]=j。
把各個頂點插入圖中,比較插點後的距離與原來的距離,G[i][j] = min( G[i][j], G[i][k]+G[k][j] ),
如果G[i][j]的值變小,則D[i][j]=k。在G中包含有兩點之間最短道路的資訊,而在D中則包含了最短通路徑的資訊。
比如,要尋找從V5到V1的路徑。根據D,假如D(5,1)=3則說明從V5到V1經過V3,路徑為{V5,V3,V1},如果D(5,3)=3,
說明V5與V3直接相連,如果D(3,1)=1,說明V3與V1直接相連。 [4] 

百度的參考程式碼:
#include<iostream>
#include<vector>
using namespace std;
const int &INF=100000000;
void floyd(vector<vector<int> > &distmap,//可被更新的鄰接矩陣,更新後不能確定原有邊
           vector<vector<int> > &path)//路徑上到達該點的中轉點
//福利:這個函式沒有用除INF外的任何全域性量,可以直接複製!
{
    const int &NODE=distmap.size();//用鄰接矩陣的大小傳遞頂點個數,減少引數傳遞
    path.assign(NODE,vector<int>(NODE,-1));//初始化路徑陣列 
    for(int k=1; k!=NODE; ++k)//對於每一箇中轉點
        for(int i=0; i!=NODE; ++i)//列舉源點
            for(int j=0; j!=NODE; ++j)//列舉終點
                if(distmap[i][j]>distmap[i][k]+distmap[k][j])//不滿足三角不等式
                {
                    distmap[i][j]=distmap[i][k]+distmap[k][j];//更新
                    path[i][j]=k;//記錄路徑
                }
}
void print(const int &beg,const int &end,
           const vector<vector<int> > &path)//傳引用,避免拷貝,不佔用記憶體空間
           //也可以用棧結構先進後出的特性來代替函式遞迴 
{
    if(path[beg][end]>=0)
    {
        print(beg,path[beg][end],path);
        print(path[beg][end],end,path);
    }
    else cout<<"->"<<end;
}
int main()
{
    int n_num,e_num,beg,end;//含義見下
    cout<<"(不處理負權迴路)輸入點數、邊數:";
    cin>>n_num>>e_num;
    vector<vector<int> > path,
          distmap(n_num,vector<int>(n_num,INF));//預設初始化鄰接矩陣
    for(int i=0,p,q; i!=e_num; ++i)
    {
        cout<<"輸入第"<<i+1<<"條邊的起點、終點、長度(100000000代表無窮大,不聯通):";
        cin>>p>>q;
        cin>>distmap[p][q];
    }
    floyd(distmap,path);
    cout<<"計算完畢,可以開始查詢,請輸入出發點和終點:";
    cin>>beg>>end;
    cout<<"最短距離為"<<distmap[beg][end]<<",列印路徑:"<<beg;
    print(beg,end,path);
}

瞭解了這個演算法以後,貼上本題的程式碼,如果a和b有關係,b和c有關係,那麼a與c就有關係,利用二維陣列來存關係:
#include<bits/stdc++.h>
using namespace std;
int dfs[1000][1000];//定義全域性變數並且賦值為0;
int main() {
	int n,m,x,y;
	cin>>n>>m;
	while(m--) {
		cin>>x>>y;
		dfs[x][y]=1;
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<=n;j++)
	for(int k=1;k<=n;k++) {
		if((dfs[j][i])&&(dfs[i][k]))
					dfs[j][k]=1;                                                                                  
	}
	for(int i=1;i<=n;i++) {
		if(dfs[i][i]) 
			cout<<"T\n";
		else
			cout<<"F\n";
	}
	return 0;
}

這是目前能整理的幾道題目,接下來還會繼續更新~