演算法課2-演算法課第一次作業總結
A:單詞翻轉 總時間限制: 1000ms 記憶體限制: 65536kB 描述: 輸入一個句子(一行),將句子中的每一個單詞翻轉後輸出。
輸入: 只有一行,為一個字串,不超過500個字元。單詞之間以空格隔開。 輸出: 翻轉每一個單詞後的字串,單詞之間的空格需與原文一致。 樣例輸入
hello world
樣例輸出
olleh dlrow
這道題算是首次用c++在oj上刷題,一開始呼叫了reverse函式,一直WA,很鬱悶,直接cout倒是AC了。
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
using namespace std;
int main() {
string sentence;
getline(cin,sentence);//使用了getline
int left=0;//單詞左邊
int right=0;//單詞右邊
while(right<sentence.size()){
//遇到空格就是一個單詞(中間多個空格也成立)
if(sentence[right]== ' ') {
for(int i=right-1;i>=left;i--) {
cout << sentence[i];
}
cout<<' ';
left=right+1;
}
if(right==sentence.size()-1){
for(int i=right;i>=left;i--) {
cout << sentence[ i];
}
}
right++;
}
return 0;
}
B:密碼 總時間限制: 1000ms 記憶體限制: 65536kB 描述 Bob 和 Alice 開始使用一種全新的編碼系統。它是一種基於一組私有鑰匙的。他們選擇了n個不同的數a1 , . . .,an, 它們都大於0小於等於n。 機密過程如下:待加密的資訊放置在這組加金鑰匙下,資訊中的字元和金鑰中的數字一一對應起來。資訊中位於i位置的字母將被寫到加密資訊的第ai個位置, ai 是位於i位置的金鑰。加密資訊如此反覆加密,一共加密 k 次。
資訊長度小於等於n。如果資訊比 n 短, 後面的位置用空格填補直到資訊長度為n。
請你幫助 Alice 和 Bob 寫一個程式,讀入金鑰,然後讀入加密次數 k 和要加密的資訊,按加密規則將資訊加密。
輸入 輸入包括幾塊。每塊第一行有一個數字n, 0 < n <= 200. 接下來的行包含n個不同的數字。數字都是大於0小於等於n的。下面每行包含一個k和一個資訊字串,它們之間用空格格開。每行以換行符結束,換行符不是要加密的資訊。每個塊的最後一行只有一個0。 最後一個塊後有一行,該行只有一個0。 輸出 輸出有多個塊,每個塊對應一個輸入塊。每個塊包含輸入中的資訊經過加密後的字串,順序與輸入順序相同。所有加密後的字串的長度都是 n。 每一個塊後有一個空行。 樣例輸入
10 4 5 3 7 2 8 1 6 10 9 1 Hello Bob 1995 CERC 0 0
樣例輸出
BolHeol b C RCE
來源 poj 1026
這道題的輸入就難倒我了,結果還不是表面上看起來的容易,需要找到密碼的迴圈節,也就是說,找到轉換多少次後又對應位置的字母又回到了原位,不然會超時。
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int n,k;
int code[210],T[210],ans[210];
bool vis[210];//類似union find
char s[210];
while(cin>>n)
{
if(n==0) break;
for(int i=1;i<=n;++i)
{
cin>>code[i];
}
memset(vis,0,sizeof(vis));//陣列初始化
for(int i=1;i<=n;++i)
{
if(vis[i])continue;
vis[i]=true;
int cnt=1;
for(int j=code[i];j!=i;j=code[j])
{
cnt++;
vis[j]=true;
}
T[i]=cnt;//找到圈,存下來
for(int j=code[i];j!=i;j=code[j])
{
T[j]=cnt;//所有的這條路徑,肯定都是再變換這麼多次就回來
}
}
while(cin>>k)
{
if(k==0) break;
getchar();//防空格
cin.getline(s+1,200);
int len=strlen(s+1);
for(int i=len+1;i<=n;++i)
{
s[i]=' ';//末尾填充空格
}
s[n+1]='\0';//末尾填充字串結尾符
for(int i=1;i<=n;++i)
{
int j=i;
for(int t=0;t<(k%T[i]);++t)//變換k%T[i]次即可
{
j=code[j];
}
ans[j]=i;
}
for(int i=1;i<=n;++i)
{
cout<<s[ans[i]];
}
cout<<endl;
}
cout<<endl;
}
return 0;
}
C:陣列轉換
總時間限制: 1000ms 記憶體限制: 65535kB 描述 有n個數字1,2,3…,n,由小至大依次放入棧。問是否存在一種出棧方式,使這n個數字出棧的順序為a1,a2,…,an。
輸入 第一行輸入一個整數t,代表有t組測試資料 對於每組測試資料,第一行輸入整數n,第二行輸入n個數字:a1,a2, … , an 1<= n <= 1000 輸出 每組測試資料輸出一行。 如果出棧順序a1,a2,…,an是合法的,輸出yes,否則輸出no。 樣例輸入
3 3 1 2 3 3 3 2 1 4 1 4 2 3
樣例輸出
yes yes no
這道題的思路是說,找一個棧直接模擬這個入棧出棧的過程,如果放進去要彈出來,就得先把前面壓進去的彈出來。我一開始的思路不太對,想著先找出那些立馬彈出來的(以為下標等於數字就行),再根據棧的彈出過程來判斷,這樣子沒有考慮全面,比如1342也可以。
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <stack>
using namespace std;
int main() {
int t;
cin>>t;
int test_num=0;
while(test_num<t){
int n;
cin>>n;
stack<int>s;
int nums[n];
for(int i=0;i<n;i++){
int num;
cin>>num;
nums[i]=num;
}
int point=0;
bool flag=true;
for(int i=0;i<n;i++)
{
//將nums[i]前面的元素都壓入棧
//point儲存壓到哪個元素了
for(int j=point+1;j<=nums[i];j++)
{
point=j;
s.push(j);
}
//不等於就不對了
if(s.top()!=nums[i]) {
flag = false;
break;
}
else {
s.pop();
}
}
if(flag) {
cout << "yes" << endl;
}
else{
cout<<"no"<<endl;
}
test_num++;
}
return 0;
}
D:漢諾塔問題(Hanoi)
總時間限制: 1000ms 記憶體限制: 65535kB 描述 一、漢諾塔問題
有三根杆子A,B,C。A杆上有N個(N>1)穿孔圓盤,盤的尺寸由下到上依次變小。要求按下列規則將所有圓盤移至C杆: 每次只能移動一個圓盤; 大盤不能疊在小盤上面。 提示:可將圓盤臨時置於B杆,也可將從A杆移出的圓盤重新移回A杆,但都必須遵循上述兩條規則。
問:如何移?最少要移動多少次?
輸入 輸入為一個整數後面跟三個單字元字串。 整數為盤子的數目,後三個字元表示三個杆子的編號。 輸出 輸出每一步移動盤子的記錄。一次移動一行。 每次移動的記錄為例如3:a->b 的形式,即把編號為3的盤子從a杆移至b杆。 我們約定圓盤從小到大編號為1, 2, …n。即最上面那個最小的圓盤編號為1,最下面最大的圓盤編號為n。 樣例輸入
3 a b c
樣例輸出
1:a->c 2:a->b 1:c->b 3:a->c 1:b->a 2:b->c 1:a->c
主要參考了上面寫的網址,使用遞迴,總體步驟就是,先把n-1個盤子藉助第3個柱子挪到第2個柱子,再把這個盤子挪到第3個柱子,然後再把n-1個盤藉助第一個盤挪到第三個盤。
#include<cstdio>
#include<cstring>
#include<vector>
#include<utility>
#include <iostream>
using namespace std;
int move(int num,char from,char temp,char to){
//遞迴的邊界條件,盤子只剩一個
if(num==1){
cout<<num<<":"<<from<<"->"<<to<<endl;
}
else{
move(num-1,from,to,temp);
cout<<num<<":"<<from<<"->"<<to<<endl;
move(num-1,temp,from,to);
}
}
int main(){
int n;
cin>>n;
char a,b,c;
cin>>a>>b>>c;
move(n,a,b,c);
return 0;
}
E:由中根序列和後根序列重建二叉樹
總時間限制: 500ms 記憶體限制: 65535kB 描述 我們知道如何按照三種深度優先次序來周遊一棵二叉樹,來得到中根序列、前根序列和後根序列。反過來,如果給定二叉樹的中根序列和後根序列,或者給定中根序列和前根序列,可以重建一二叉樹。本題輸入一棵二叉樹的中根序列和後根序列,要求在記憶體中重建二叉樹,最後輸出這棵二叉樹的前根序列。
用不同的整數來唯一標識二叉樹的每一個結點,下面的二叉樹
中根序列是9 5 32 67
後根序列9 32 67 5
前根序列5 9 67 32
輸入 兩行。第一行是二叉樹的中根序列,第二行是後根序列。每個數字表示的結點之間用空格隔開。結點數字範圍0~65535。暫不必考慮不合理的輸入資料。 輸出 一行。由輸入中的中根序列和後根序列重建的二叉樹的前根序列。每個數字表示的結點之間用空格隔開。 樣例輸入
9 5 32 67 9 32 67 5
樣例輸出
5 9 67 32
碰到樹的問題首先想遞迴,我的思路是從後序遍歷中先找到根(最後一個元素),再從確定中序遍歷的左右子樹內容,對左右子樹遞迴。
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
void rebuild(int mid[],int back[],int l1,int r1, int l2,int r2){
//輸出根節點
cout<<back[r2]<<" ";
//停止條件,如果只剩一個元素就結束
if(l1-r1==0)
return;
//找到根節點位置
int point1=-1;
for(int i=l1;i<=r1;i++){
if(back[r2]==mid[i]){
point1=i;
}
}
//有左子樹
if(point1-1>=0) {
int l2_new = -1;
int r2_new = -1;
for (int i = l2; i <= r2; i++) {
if (back[i] == mid[l1]) {
l2_new = i;
}
if (back[i] == mid[point1 - 1]) {
r2_new = i;
}
}
rebuild(mid, back, l1, point1 - 1, l2_new, r2_new);
}
//有右子樹
if(mid[point1+1]>0) {
int l2_new = -1;
int r2_new = -1;
for (int i = l2; i <= r2; i++) {
if (back[i] == mid[point1 + 1]) {
l2_new = i;
}
if (back[i] == mid[r2]) {
r2_new = i;
}
}
rebuild(mid, back, point1 + 1, r2, l2_new, r2_new);
}
}
int main() {
int i=0;
int mid[65536];
int back[65536];
while(cin>>mid[i++])
{
if(cin.get() != ' ') break;
}
i=0;
while(cin>>back[i++])
{
if(cin.get() != ' ') break;
}
rebuild(mid,back,0,i-1,0,i-1);
return 0;
}
F:區間合併
總時間限制: 1000ms 記憶體限制: 65536kB 描述 給定 n 個閉區間 [ai; bi],其中i=1,2,…,n。任意兩個相鄰或相交的閉區間可以合併為一個閉區間。例如,[1;2] 和 [2;3] 可以合併為 [1;3],[1;3] 和 [2;4] 可以合併為 [1;4],但是[1;2] 和 [3;4] 不可以合併。
我們的任務是判斷這些區間是否可以最終合併為一個閉區間,如果可以,將這個閉區間輸出,否則輸出no。
輸入 第一行為一個整數n,3 ≤ n ≤ 50000。表示輸入區間的數量。 之後n行,在第i行上(1 ≤ i ≤ n),為兩個整數 ai 和 bi ,整數之間用一個空格分隔,表示區間 [ai; bi](其中 1 ≤ ai ≤ bi ≤ 10000)。 輸出 輸出一行,如果這些區間最終可以合併為一個閉區間,輸出這個閉區間的左右邊界,用單個空格隔開;否則輸出 no。 樣例輸入
5 5 6 1 5 10 10 6 9 8 10
樣例輸出
1 10
一開始就想到要排序,但是c++的結構體不會弄,又學習了一波。
#include <stdio.h>
#include<iostream>
#include <string.h>
#include <string>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
//定義結構體 oj常用
struct area{
int upper;
int lower;
};
//定義比較函式
int cmp(area x1, area x2){
return x1.lower<x2.lower;
}
int main() {
vector<int> v1;
vector<int> v2;
int n;
cin>>n;
area a[n];
int areanum=0;
while(areanum<n){
int lower;
cin>>lower;
int upper;
cin>>upper;
a[areanum].lower=lower;
a[areanum].upper=upper;
areanum++;
}
//排序用法
sort(a,a+n,cmp);
int l=a[0].lower;
int h=a[0].upper;
for(int i=0;i<n;i++){
if(a[i].lower>h){
cout<<"no";
return 0;
}
else{
h=max(a[i].upper,h);
}
}
cout<<l<<" "<<h;
return 0;
}
主要學習oj上常用的c++語法,和一些思路(比如迴圈節),收穫挺大的,繼續努力。