述:大二初一次簡單的測試
經過在暑假一個月的學習,大二第一學期開學進行了學習結果測試
(2017.9.26 週二下午三點到七點的測試,中午睡過了,影響了半個小時的時間,沒有搶到先機有點尷尬。。。)
一共有六道題,沒有具體難度區分;來的時候比較緊急大概瀏覽了一邊,發現第一題的括號配對和第四題的會場安排都在暑假的時候做過了;有一道英文題(因為英語水平不高,心中暗道還好還好)心情也由來晚稍稍放鬆了一點,先拿到第一題就開始做了。
先來說說題目吧:
第一題:
從題目來看是對於資料結構的簡單應用,棧的push(放入),top(頂部元素),pop(丟掉);遵循先進後出的原則進行處理資料;
所以就可以把問題簡單化,左括號直接放入棧中,右括號則進行if判斷,如果滿足於棧的top(頂)結合,然後就pop(把棧頂的元素丟掉);
有了基本思路就開始著手打程式碼;
#include<iostream> #include<stack> #include<cstdio> #include<cstring> using namespace std; int main() { char k[110]; int m,i,n; scanf("%d", &m); while (m--) { scanf("%s", k); n = strlen(k); int h = 0; stack <char>s; s.push('#'); if (n == 0 || n % 2 != 0) h = 1; else { for (i = 0; i < n; i++) { if (k[i] == '(' || k[i] == '[' || k[i] == '{' || k[i] == '<') { s.push(k[i]); } else if (k[i] == ')') { if (s.top() == '(') s.pop(); else break; } else if (k[i] == ']') { if (s.top() == '[') s.pop(); else break; } else if (k[i] == '}') { if (s.top() == '{') s.pop(); else break; } else if (k[i] == '>') { if (s.top() == '<') s.pop(); else break; } } } if(h!=0) printf("No\n"); else if (s.top() == '#') printf("Yes\n"); else printf("No\n"); } return 0; }
是不是看著太麻煩了,這是當時自己做的,後來發現其實else if 就是不需要的,增加了程式碼的長度並減少了可閱讀性;
而且這樣分段寫,需要多注意的是在考慮了左括號直接壓入棧後是可以正常計算,但是一開始的是右括號呢,一直都是右括號的情況,我就疏忽了這個(好像當時還沒睡醒,有點迷迷糊糊的)直道提交了四次才意識到,真的失誤了,有了教訓其他的都是一邊過的。
還是學長的答案看著比較舒服。。。 來分享一下:
#include <cstring> #include <cstdio> #include <stack> char s[10005]; int main() { int n; scanf("%d", &n); while (n--) { int len, i; std::stack <char> c; scanf("%s", s); len = strlen(s); for (int i = 0; i < len; i++) { if (c.empty()) c.push(s[i]); else { if((s[i]==')'&&c.top()=='(')||(s[i]==']'&&c.top()=='[') || (s[i]=='>'&&c.top()=='<') || (s[i]=='}'&&c.top()=='{')) c.pop(); else c.push(s[i]); } } if (!c.empty()) printf("No\n"); else printf("Yes\n"); } }
思路是一樣的,但是在處理右括號的方面有了很大的改進,把逐句判斷寫成了同步判斷然後||語句連結,把所有情況都考慮了進去。還是比較厲害的,對於新手的我來說很值得借鑑學習。
第二題:
做這道題的關鍵就是看清題,看清題!!! 清楚了怎麼分給下個人的那麼這題是很簡單的。 是每個人同時去除一半然後同時給右邊的那位,把最後一位單一考慮就可以了。
當時好像浪費了一個小時在看錯題上了,已知資料不過才仔細看題意識到,不是拿了給右邊的,然後右邊的再拿出現有的一半給右邊的朋友; 唉,這兩道滿滿的累,拖了好長時間才搞定,本來很簡單的兩道題,細心才是最重要的。
#include<stdio.h>
#include<string.h>
int k[10001],t[10001],n,sum;
int fun()
{
int i,max=k[0];
for(i=0;i<n;i++)
{
if(k[i]!=max)
return 0;
}
return 1;
}
int main()
{
int i,max;
while(~scanf("%d",&n))
{
if(n==0)
break;
sum=0;
for(i=0;i<n;i++)
{
scanf("%d",&k[i]);
}
if(fun())
printf("%d %d\n",sum,k[0]);
else
{
while(1)
{
for(int i=0;i<n;i++)
{
t[i]=k[i]/2;
k[i]=k[i]/2;
}
for(int i=1;i<n;i++)
{
k[i]=k[i]+t[i-1];
if(k[i]%2!=0)
k[i]=k[i]+1;
}
k[0]=t[n-1]+k[0];
if(k[0]%2!=0)
k[0]=k[0]+1;
sum++;
if(fun())
break;
}
printf("%d %d\n",sum,k[0]);
sum=0; memset(k,0,n+1);
}
}
return 0;
}
程式碼寫得複雜了,因為一直是在原先想錯方向之後改正的,直接加了一個數組來存放每位朋友拿出來的一半,每次迴圈都作出更新。
還好測試資料不大就直接過了;
看看學長寫的做下比較:
#include <cstdio>
#include <cstring>
int children[105];
int main()
{
int n;
while (scanf("%d", &n) && n) {
memset(children, 0, sizeof(children));
for (int i = 0; i < n; i++) {
scanf("%d", &children[i]);
}
int cnt = 0;
while (true) {
bool flag = false;
int dot;
for (int i = 0; i < n - 1; i++) {
if (children[i] != children[i + 1]) {
flag = true;
break;
}
}
if (!flag) {
break;
}
cnt++;
int sweet = children[0] / 2;
for (int i = 0; i < n; i++) { //這裡可以優化一下 想想怎麼優化。
children[i] /= 2;
if (i != n - 1) {
children[i] += children[i + 1] / 2;
}
else {
children[i] += sweet;
}
}
for (int i = 0; i < n; i++) {
if (children[i] % 2) children[i]++;
}
}
printf("%d %d\n", cnt, children[0]);
}
return 0;
}
因為迴圈往往是很耗時的,所以可以優化問題,儘可能減少迴圈巢狀或者直接不用迴圈;
第三題:
英語描述了一大段看著挺嚇人的,其實看了一眼輸入,輸出和這個溫馨的提示。就瞬間明白了要求是找10000以內所有滿足提示所展示的數字 等於另一個數字的各位數字和本身的和; 若是這樣正向找的話,估計又要好多迴圈開始挨個查詢。 所以看到這裡不如反向的想想,各位數字和本身相加得到的一位數字記下來就是滿足selfs number的;
而這道題就可以用一個10000的陣列下標來記錄已經找到的,然後用迴圈輸出沒有標記的就是答案了;
#include<stdio.h>
int k[10005]={0};
int main()
{
int i,j,m,n;
for(i=0;i<10000;i++)
{
j=i+i%10+i/10%10+i/10/10%10+i/10/10/10%10;
k[j]=1;
}
for(i=0;i<10000;i++)
{
if(k[i]==0)
printf("%d\n",i);
}
}
做這道題想到了nyoj上的 無主之地 方法比較類似
雖然自己的這個已經有點自信了,看到學長的更加簡潔;
#include<cstdio>
#include<cstring>
using namespace std;
int num[20010];
int main()
{ int i,j,k;
for(i=1;i<=10000;i++)
{ num[i/1000+i/100%10+i/10%10+i%10+i]=1;
if(!num[i])
printf("%d\n",i);
}
}
第四題:
這道題考察的就是貪心演算法了,很常見的一道;在暑假的時候也做過類似的;簡化為區間覆蓋問題,利用結構體,把左右端點整體都存下來,然後按左端點排序,若左端點相同則按右端點升序排列;實現還是比較簡單的:
#include<iostream>
#include<algorithm>
#include<stdio.h>
using namespace std;
struct node{
int x,y;
};
bool fr(node a,node b)
{
return a.x==b.x?a.y<b.y:a.x<b.x;
}
int main()
{
int m,n,i,sum,p;struct node k[10010];
scanf("%d",&m);
while(m--)
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d%d",&k[i].x,&k[i].y);
}
sort(k,k+n,fr);p=k[0].y;sum=1;
for(i=1;i<n;i++)
{
if(k[i].x>p)
{
sum++;p=k[i].y;
}
else if(k[i].y<p)
{
p=k[i].y;
}
}
printf("%d\n",sum);
}
}
想想當時剛開始接觸貪心的時候可是熬夜通宵打一道題,還是熟能生巧比較實在;
看看學長的程式碼,這次在排序程式碼的簡潔上略微有點優勢哦(笑笑。。哈哈哈)
#include <cstdio>
#include <algorithm>
using namespace std;
struct Time{
int a;
int b;
};
struct Time s[10010];
int compare(struct Time a, struct Time b){
if(a.b != b.b){
return a.b < b.b;
}
return a.a < b.a;
}
int main(){
int n, t, i, j, k, ans, end;
scanf("%d", &t);
for(i=1; i<=t; i++){
scanf("%d", &n);
for(j=0; j<=n-1; j++){
scanf("%d%d", &s[j].a, &s[j].b);
}
ans=0;
end=-1;
sort(s, s+n, compare);
for(j=0; j<=n-1; j++){
if(s[j].a>end){
ans++;
end=s[j].b;
}
}
printf("%d\n", ans);
}
return 0;
}
第五題:
這道題用到了貪心+二分法;貪心找最大方案,尋找工具是二分查詢(由於資料較大而且精度較高,二分不會超時,而且貪心和二分是很好的組合~)
先明白題目的意思:把已知大小的蛋糕分給若干人,要取得最大化,把最大的一塊蛋糕先找出來,當作二分的右邊界,左邊界初始為0,把中值當作此時要分的蛋糕最大值,看是否符合人數需要,若是滿足,則把左值右移到中值,擴大中值,以此類推找到最合適的中值,也就是最大的蛋糕分配;(附:PI = 3.1415926535897932)
#include<stdio.h>
#include<algorithm>
#define PI 3.1415926535897932
using namespace std;
int n,f; double v[10005];
bool fun(double mid)
{
int m=0;double a;
for(int i=n-1;i>=0;i--)
{
a=v[i];
while(a>=mid)
{
a=a-mid;
m++;
}
}
if(m>=f)
return true;
else
return false;
}
int main()
{
int i,m;double a;
scanf("%d",&m);
while(m--)
{
scanf("%d%d",&n,&f);f++;
for(i=0;i<n;i++)
{
scanf("%lf",&a);
v[i]=a*a*PI;
}
sort(v,v+n);
double l=0,r=v[n-1],mid;
while(r-l>=0.00000001)
{
mid=(l+r)/2;
if(fun(mid))
l=mid;
else
r=mid;
}
printf("%.4lf\n",mid);
}
return 0;
}
這個是考試完寫的了,前面用的時間太久了; 看看學長的程式碼:
#include<iostream>
#include <cstdio>
using namespace std;
const double PI = 3.1415926535897932;
const int MAXN = 1005;
const double ESP=1e-6;
double v[MAXN];
int main(void)
{
int test;
cin>>test;
while(test--)
{
int n,f;
cin>>n>>f;
f++;
double maxsize = 0.0;
for(int i=1;i <= n;i++)
{
cin >> v[i];
v[i] *= v[i];
if( maxsize < v[i])
maxsize = v[i];
}
double low = 0.0;
double high = maxsize;
double mid;
while(high - low > ESP)
{
mid = (low + high) / 2;
int count_f = 0;
for(int i = 1; i <= n; i++)
count_f += (int)(v[i] / mid); //當前半徑下,每一塊蛋糕可以分成幾份
if(count_f < f)
high = mid; //因為求的是高精度的小數 所以high最好是等於mid,我試過high=mid+ESP,提交錯誤,應該是ESP的精度太小的原因。
else
low = mid;
}
printf("%.4lf\n", mid * PI);
}
return 0;
}
沒有呼叫函式,而是直接寫到了迴圈體裡面;看著更加方便了一些;
第六題:
這道題用到了深搜,可是我還不會,也沒寫出來,就先附上學長的程式碼,等會了再來補充吧。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=10;
char board[maxn][maxn];
int isPut[maxn]; //判斷某一列是否可以放棋子
int n,k;
int cnt,num; //cnt方案數 num 棋子數
void DFS(int i){
if(k==num){
cnt++;
return ;
}
if(i>=n)
return ;
for(int j=0;j<n;j++)
if(!isPut[j] && board[i][j]=='#'){
isPut[j]=1;
num++;
DFS(i+1);
isPut[j]=0; //回溯,DFS很重要的一步
num--;
}
DFS(i+1);
}
int main(){
int i,j;
while(scanf("%d%d",&n,&k) && (n != -1 && k != -1)){
//getchar();
for(i=0;i<n;i++){
for(j=0;j<n;j++)
//scanf("%c",&board[i][j]);
//getchar();
cin >> board[i][j];
}
memset(isPut,0,sizeof(isPut));
cnt=0;
num=0;
DFS(0);
printf("%d\n",cnt);
}
return 0;
}
2017.9.27 (第一次寫的個人部落格)