1. 程式人生 > >2017北京林業大學「計蒜客杯」程式設計競賽部分題解

2017北京林業大學「計蒜客杯」程式設計競賽部分題解

偶數加成記

時間限制: 2000ms  記憶體限制: 256M

描述

xyb這裡有n個整數。xyb想要得到這n個整數最大的偶數和(即該和為偶數),每個整數最多隻能加一次。請你計算出最大的這個值。

輸入

輸入包含多組。輸入的第一個數為一個n1<=n<=100000)。下一行包括xybn個整數,以空格隔開。每個整數的範圍為1~10^9

輸出

請輸出最大的偶數和。

樣例輸入複製

3

1 2 3

樣例輸出1

6

樣例輸入複製

5

999999999 999999999 999999999 999999999999999999

樣例輸出2

3999999996

解題思路:

偶數全部加起來,看奇數多少個,如果是奇數個那麼把最小的那個剔除,如果偶數個則全部相加。

AC程式碼:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <set>
#include <deque>
#include <functional>
#define maxn 100100
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll a[maxn];
int main()
{
   int n;
   while (~scanf("%d", &n))
   {
      ll sum = 0;
      int cnt = 0;
      for (int i = 1; i <=n; i++)
      {
          ll x;
          scanf("%lld", &x);
          if (x & 1)
             a[cnt++]= x;
          else sum += x;
      }
      sort(a, a + cnt, greater<ll>());
      if (cnt & 1)
      {
          for (int i = 0; i <cnt - 1; i++)
             sum +=a[i];
      }
      else
      {
          for (int i = 0; i <cnt; i++)
             sum +=a[i];
      }
      printf("%lld\n", sum);
   }
   return 0;
}


理智逃稅

時間限制: 2000ms  記憶體限制: 256M

描述

xyb在一個高大上的國家工作,這個國家有一個特殊的稅收政策。若xyb每年的收入為n元(n>=2),那麼xyb所要交的稅為n的所有約數中的最大值(不包括n本身。)比如,如果xyb一年收入為6元,那麼他就需要交稅3元;如果xyb一年收入為25元,那麼他就需要交稅5元;如果xyb一年收入為2元,那麼他就需要交稅1元。

然而xyb是個機(jiao)智(hua)的人,他想要上交的稅越少越好。所以,他決定,將每年的收入n元,拆分一下:n1+n2+...+nk=n(這裡的k表示拆分的份數,k=1也是可以的),然後再將每份所要交的稅一起上交即可。當然,對於每份的錢數來說,不能少於

2元(如果某一份少於2元他就會被正直的人給揭發了!)

xyb想知道他最少只需要交多少錢的稅,你快來幫幫他吧!

輸入

輸入第一行包含一個整數n2<=n<=2*10^9),表示xyb的總錢數。

輸出

輸出一個整數,表示xyb最少需要交多少稅。

樣例輸入複製

4

樣例輸出1

2

樣例輸入複製

27

樣例輸出2

3

解題思路:

       基於哥德巴赫猜想。“對於偶數:一個大於2的偶數可以分為兩個質數的和;對於奇數:一個奇數n且不為質數,若其n-2為質數,那麼n可以分為兩個質數的和,若n-2不為質數,那麼n可以分為三個質數的和。”

AC程式碼:

#include<cstdio>
#include<iostream>
using namespace std;
bool isprime(int n)
{
   for (int i = 2; i*i<=n; i++)
      if (n%i == 0)
          return false;
   return true;
}
int main()
{
   int n;
   while (cin >> n)
   {
      if (isprime(n))
      {
          printf("1\n");
          continue;
      }
      if (n % 2 == 0 || isprime(n - 2))
      {
          printf("2\n");
          continue;
      }
      printf("3\n");
   }
   return 0;
}


我要吃粽子

時間限制: 1000ms  記憶體限制: 128M

描述

快看!高空中漂浮著好多粽子,而且越高的粽子越大!

好神奇,我要吃到最大的粽子!

地上零零散散放著K種磚塊(這些磚塊能幫我們吃到高處的粽子)。對於第i類磚塊的描述:

1. i類磚塊,每塊高度皆為H

2. i類磚塊共有Q個。

3. i類磚塊,凡是放到高於L的高空會自動消失。

用這些磚塊一塊塊堆砌,以此爬向天空。為了使吃到的粽子儘可能大,問我最高能爬到多高的高空?

輸入

有多組資料每組輸入,第一行為一個整數K(1 <= K <= 400)第二行為k+1行,每行三個空格分隔的整數H(1 <= H <= 100)L (1 <= L <= 40000)Q(1 <= Q<= 10),表示第i種磚塊的HLQ

輸出

每組輸出一個整數h,表示能吃到的最大粽子所在高度。

樣例輸入複製

3

7 40 3

5 23 8

2 52 6

樣例輸出1

48

解題思路:

考慮多重揹包,不同的是:每次放一個磚要考慮下面又沒有東西,不能放在空中

所以先要根據最高度,排序。然後用多重揹包遞推求解。遞推公式:

dp[i][j]=dp[i][j-h[i]] 和 used[i][j]=used[i][j-h[i]]+1 (其中i表示第i種磚塊,j表示高度轉移,再用used記錄一下第i種磚塊用的個數)

AC程式碼:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 40010
#define lson root << 1
#define rson root << 1 | 1
#define lent (t[root].r -t[root].l + 1)
#define lenl (t[lson].r - t[lson].l + 1)
#define lenr (t[rson].r - t[rson].l + 1)
#define N 404
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
typedef unsigned long longull;
int dp[maxn], used[maxn];
struct node
{
   int h, l, q;
   friend bool operator< (node a,node b)
   {
      return a.l < b.l;
   }
} p[N];
int main()
{
#ifndef ONLINE_JUDGE
   freopen("in.txt","r", stdin);
   freopen("out.txt","w", stdout);
   long _begin_time = clock();
#endif
   int k;
   while (~scanf("%d", &k))
   {
      for (int i = 0; i <k; i++)
          scanf("%d%d%d", &p[i].h, &p[i].l, &p[i].q);
      sort(p, p + k);
      memset(dp, 0, sizeof(dp));
      dp[0] = 1;
      for (int i = 0; i <k; i++)
      {
          memset(used, 0,sizeof(used));
          for (int j = p[i].h; j <= p[i].l; j++)
          {
             if (used[j - p[i].h] < p[i].q)
             {
                if (!dp[j]&& dp[j - p[i].h])
                {
                   dp[j]= dp[j - p[i].h];
                   used[j]= used[j - p[i].h] + 1;
                }
             }
          }
      }
      int ans = 0;
      for (int i =maxn; i >= 0;i--)
          if (dp[i])
          {
             ans =i;
             break;
          }
      printf("%d\n", ans);
   }
#ifndef ONLINE_JUDGE
   long _end_time = clock();
   printf("time =%ld ms.", _end_time -_begin_time);
#endif
   return 0;
}


Universe7去約會

時間限制: 1000ms  記憶體限制: 128M

描述

Universe7有個非常漂亮的女朋友,他每天都要和她去約會(嫌虐狗?上週末幹啥去了!)。他的女友為了考驗他,每次都選一個非常神奇的迷宮等他。在迷宮裡有許多隻蝸牛,如果踩到了蝸牛就會暴斃而亡。但蝸牛會在隔一定的時間消失一秒。其餘的時間蝸牛就會待在那裡等待大傻子去踩。

下面給出迷宮的地圖圖例。'.'代表可以走的路,'#'代表蝸牛,'U'代表Universe7的位置,'G'代表他女朋友的位置。每一秒鐘,Universe7都可以向上向下向左向右移動一格。

輸入

第一行給出樣例的總數T(0<T<20),接下來有T個樣例。每個樣例給出迷宮的行數r列數c和蝸牛消失的間隔時間k(1<=r,c<=100,2<=k<=10)。接下來的r行代表迷宮的地圖。

輸出

如果Universe7可以成功約會,輸出最短時間,否則輸出"Please give me another chance!".

樣例輸入複製

2

6 6 2

...U..

...#..

.#....

...#..

...#..

..#G#.

4 4 2

U###

####

####

###G

樣例輸出1

7

Please give me another chance!

解題思路:

       帶時間狀態的廣搜,考慮開三層vis[x][y][z]其中前兩層表示座標,第三層表示時間狀態,當t%k==0則表示可進狀態。

AC程式碼:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 100010
#define lson root << 1
#define rson root << 1 | 1
#define lent (t[root].r -t[root].l + 1)
#define lenl (t[lson].r - t[lson].l + 1)
#define lenr (t[rson].r - t[rson].l + 1)
#define N 1111
#define eps 1e-6
#define pi acos(-1.0)
#define e exp(1.0)
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
typedef unsigned long longull;
char mp[110][110];
bool vis[110][110][11];
int dir[4][2] = { { 1, 0 },{ -1, 0 },{ 0, -1 },{0, 1 } };
int r, c, k, ans;
struct node
{
   int x, y, t;
   node(int a, int b, int c) { x =a; y = b; t = c; }
};
void bfs(int stax,int stay)
{
   memset(vis, 0, sizeof(vis));
   queue<node> q;
   q.push(node(stax,stay, 0));
   vis[stax][stay][0] = 1;
   while (!q.empty())
   {
      node cur = q.front();
      q.pop();
      if (mp[cur.x][cur.y] =='G')
      {
          ans = cur.t;
          return;
      }
      for (int i = 0; i <4; i++)
      {
          int dx = cur.x + dir[i][0];
          int dy = cur.y + dir[i][1];
          int time = cur.t + 1;
          if (dx >= 0&& dx < r && dy >= 0 && dy < c &&(mp[dx][dy] !='#' || time % k == 0) &&!vis[dx][dy][time % k])
          {
             vis[dx][dy][time% k] = 1;
             q.push(node(dx, dy, time));
          }
      }
   }
}
int main()
{
#ifndef ONLINE_JUDGE
   freopen("in.txt","r", stdin);
   freopen("out.txt","w", stdout);
   long _begin_time = clock();
#endif
   int t;
   scanf("%d", &t);
   while (t--)
   {
      scanf("%d%d%d", &r,&c, &k);
      int stax, stay;
      bool flag = 0;
      for (int i = 0; i <r; i++)
      {
          scanf("%s", mp[i]);
          for (int j = 0; j < c&& !flag; j++)
             if (mp[i][j] == 'U')
             {
                stax= i;
                stay= j;
                flag= 1;
                break;
             }
      }
      ans = INF;
      bfs(stax, stay);
      if (ans < INF)
          printf("%d\n", ans);
      else
          puts("Pleasegive me another chance!");
   }
#ifndef ONLINE_JUDGE
   long _end_time = clock();
   printf("time =%ld ms.", _end_time -_begin_time);
#endif
   return 0;
}


讀書好多讀書讀好書

時間限制: 2000ms  記憶體限制: 256M

描述

xyb剛度過一段繁忙的時期,終於有空閒的時間來讀(wan)書(shua)了。今天,他有t分鐘的空閒時間來讀書。因此,他跑到了圖書館開始他的讀書大計。圖書館中有n本書,編號從1~nxyb讀完每本書所花的時間為ai分鐘。

xyb決定從隨機的一本書開始讀,然後一本接一本的讀下去。比如,假設xyb決定從第i本書開始讀,那麼他的讀書順序編號就是i+1i+2...如果他把自己的空閒時間花光了,那他就不會再讀下去了。所以,xyb想請你幫他確定一下讀的第一本書的編號,使得他所能讀完的書的本數最大(若最後一本書他讀不完,則這本書不能算進去),輸出他所能讀的書本數的最大值。

輸入

第一行包含兩個整數nt1<=n<=10^51<=t<=10^9——n表示圖書館的書本數,t表示xyb的空閒時間(分鐘)。第二行包含n個整數a1,a2,...,an1<=ai<=10^4),ai表示xyb讀完第i本書所需時間(分鐘)。

輸出

輸出一個整數,表示xyb所能讀的書本數的最大值。

樣例輸入複製

4 5

3 1 2 1

樣例輸出1

3

樣例輸入複製

3 3

2 2 3

樣例輸出2

1

解題思路:用陣列存下字首和,然後遍歷過程中,二分往前找。

AC程式碼:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <set>
#include <deque>
#include <functional>
#define maxn 100100
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll a[maxn];
ll sum[maxn];
int main()
{
   int n, t;
   while (~scanf("%d%d", &n,&t))
   {
      sum[0] = 0;
      memset(a, 0, sizeof a);
      for (int i = 1; i <=n; i++)
      {
          scanf("%lld", &a[i]);
          sum[i] =sum[i - 1] + a[i];
      }
      int ans = 0;
      for (int i = 1; i <=n; i++)
      {
          ll res = sum[i] -t;
          if (res <= 0)
             ans =i;
          else
          {
             int l = 0, r = i;
             while (l <= r)
             {
                int mid = l + r>> 1;
                if (sum[mid] >=res)
                {
                   ans= max(ans, i - mid);
                   r= mid - 1;
                }
                else l = mid + 1;
             }
          }
      }
      printf("%d\n", ans);
   }
   return 0;
}


尋找flash

時間限制: 1000ms  記憶體限制: 128M

描述

眾所周知,flash學長很喜歡玩遊戲。今天,他想和大家玩一個捉迷藏的遊戲。flash學長會藏在一個矩陣中並且可以使自己名字中的任意字母大寫,現在看聰明的你能不能找到flash了。

(在這個矩陣中,如果將字母橫著,豎著或者斜著連在一起能組成flash的任意組合便認為是找到了flash)

輸入

輸入有多組。每組第一個行為兩個整數數nmn,m<=10),代表矩陣有nm列。接下來是一個n*m大小的矩陣。

輸出

如果flash藏在這個矩陣中,輸出yes,否則輸出no

樣例輸入複製

2 5

FeeSh

alabb

1 5

falsh

樣例輸出1

yes

no

解題思路:

因為n和m很小,直接搜一下就行,dfs或者bfs

AC程式碼:

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <set>
#include <deque>
#include <functional>
#define maxn 100100
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
char mp[20][20];
bool vis[20][20];
int dir[8][2] = { { 1, 0 },{ 0, 1 },{ -1, 0 },{0, -1 },{ -1, -1 },{ -1, 1 },{ 1, -1 },{ 1, 1 } };
int n, m, flag;
struct node
{
   int x, y, state;
   node(int a, int b, int c) {
      x = a;
      y = b;
      state = c;
   }
};
bool judge(int x, int y,int state)
{
   if (state == 2)
      return mp[x][y] == 'L' || mp[x][y] == 'l';
   else if (state == 3)
      return mp[x][y] == 'A' || mp[x][y] == 'a';
   else if (state == 4)
      return mp[x][y] == 'S' || mp[x][y] == 's';
   return mp[x][y] == 'H' || mp[x][y] == 'h';
}
void bfs(int sx, int sy)
{
   memset(vis, 0, sizeof(vis));
   queue<node> q;
   q.push(node(sx, sy, 1));
   while (!q.empty())
   {
      node cur = q.front();
      q.pop();
      if (cur.state == 5)
      {
          flag = 1;
          return;
      }
      vis[cur.x][cur.y] = 1;
      for (int i = 0; i <8; i++)
      {
          int dx = cur.x + dir[i][0];
          int dy = cur.y + dir[i][1];
          int state = cur.state + 1;
          if (dx >= 0&& dy >= 0 && dx < n && dy < m &&!vis[dx][dy])
          {
             if (judge(dx, dy, state))
                q.push(node(dx, dy, state));
          }
      }
   }
}
int main()
{
   while (~scanf("%d%d", &n,&m))
   {
      memset(mp, 0, sizeof(mp));
      for (int i = 0; i <n; i++)
          scanf("%s", mp[i]);
      flag = 0;
      for (int i = 0; i < n&& !flag; i++)
          for (int j = 0; j < m&& !flag; j++)
             if (mp[i][j] == 'F' || mp[i][j] == 'f')
                bfs(i, j);
      if (flag)
          puts("yes");
      else
          puts("no");
   }
   return 0;
}


知山知水,樹木樹人

時間限制: 1000ms  記憶體限制: 256M

描述

作為一名北林學子,知山知水,樹木樹人的校訓肯定是牢記心中的。xyb也牢記這點。現在,他就在研究k叉樹的問題。

對於k叉樹,xyb給出了他自己的定義:

1.每個節點(除葉子節點外)都有k個子節點;

2.每條邊都具有權值,每個節點下的第i條邊的權值為i

例如,如果是一棵3叉樹:

此刻,xyb提出了一個有趣的問題:從根節點出發,有多少條路徑的權值和(即這條路徑上所有的權值加起來)是等於n的,且這些路徑還要滿足一個條件,即經過的所有邊,至少有一條邊的權值是大於等於d的。

現在這個問題交給你來解決吧,由於路徑數可能會非常大,輸出時請對1000000007 (10^9+7)取模。(即%1000000007

輸入

輸入為一行。包括三個整數nkd1<=nk<=1001<=d<=k),用空格隔開。

輸出

輸出為一行。即滿足條件的路徑數對1000000007取模的結果。

樣例輸入複製

3 3 2

樣例輸出1

3

樣例輸入複製

3 3 3

樣例輸出2

1

樣例輸入複製

4 3 2

樣例輸出3

6

樣例輸入複製

4 5 2

樣例輸出4

7

解題思路:

dp[i][j],i存到和,j==0時,表示到i為止,還沒出現過>=d的數,反之,j==1時,則出現過。

然後每層dp一下,狀態轉移為:

if(j>=d)

dp[i][1]=(dp[i][1]+dp[i-j][0]+dp[i-j][1])%mod

else

dp[i][0]=(dp[i][0]+dp[i-j][0])%mod

dp[i][1]=(dp[i][1]+dp[i-j][1])%mod

AC程式碼:

#include <bits/stdc++.h>
#define maxn 100100
#define INF 0x3f3f3f3f
#define N 1010
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
ll dp[110][2];
int main()
{
   int n, k, d;
   while (~scanf("%d%d%d%d", &n,&k, &d))
   {
      memset(dp, 0, sizeof(dp));
      dp[0][0] = 1;
      for (int i = 1; i <=n; i++)
      {
          for (int j = 1; j <=min(k, i); j++)
          {
             if (j >= d)
                dp[i][1]= (dp[i][1] + dp[i - j][0] + dp[i - j][1]) % mod;
             else
             {
                dp[i][1]= (dp[i][1] + dp[i - j][1]) % mod;
                dp[i][0]= (dp[i][0] + dp[i - j][0]) % mod;
             }
          }
      }
      cout << dp[n][1] % mod << endl;
   }
   return 0;
}