1. 程式人生 > >18.5.24 動規作業

18.5.24 動規作業

The eas 出發 urn 浪費 values 加法 後來 開始

A:UNIMODAL PALINDROMIC DECOMPOSITIONS

描述

A sequence of positive integers is Palindromic if it reads the same forward and backward. For example:
23 11 15 1 37 37 1 15 11 23
1 1 2 3 4 7 7 10 7 7 4 3 2 1 1
A Palindromic sequence is Unimodal Palindromic if the values do not decrease up to the middle value and then (since the sequence is palindromic) do not increase from the middle to the end For example, the first example sequence above is NOT Unimodal Palindromic while the second example is.


A Unimodal Palindromic sequence is a Unimodal Palindromic Decomposition of an integer N, if the sum of the integers in the sequence is N. For example, all of the Unimodal Palindromic Decompositions of the first few integers are given below:
1: (1)
2: (2), (1 1)
3: (3), (1 1 1)
4: (4), (1 2 1), (2 2), (1 1 1 1)

5: (5), (1 3 1), (1 1 1 1 1)
6: (6), (1 4 1), (2 2 2), (1 1 2 1 1), (3 3),
(1 2 2 1), ( 1 1 1 1 1 1)
7: (7), (1 5 1), (2 3 2), (1 1 3 1 1), (1 1 1 1 1 1 1)
8: (8), (1 6 1), (2 4 2), (1 1 4 1 1), (1 2 2 2 1),
(1 1 1 2 1 1 1), ( 4 4), (1 3 3 1), (2 2 2 2),
(1 1 2 2 1 1), (1 1 1 1 1 1 1 1)

Write a program, which computes the number of Unimodal Palindromic Decompositions of an integer.

輸入Input consists of a sequence of positive integers, one per line ending with a 0 (zero) indicating the end.輸出For each input value except the last, the output is a line containing the input value followed by a space, then the number of Unimodal Palindromic Decompositions of the input value. See the example on the next page.樣例輸入

2
3
4
5
6
7
8
10
23
24
131
213
92
0

樣例輸出

2 2
3 2
4 4
5 3
6 7
7 5
8 11
10 17
23 104
24 199
131 5010688
213 1055852590
92 331143

提示

N < 250

來源G

reater New York 2002

技術分享圖片
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string.h>
 4 #include <algorithm>
 5 #include <stdlib.h>
 6 #include <math.h>
 7 
 8 using namespace std;
 9 
10 const int maxn = 255;
11 unsigned dp[maxn][maxn];
12 int n, maxnow;
13 
14 void dpsolve() {
15     for (int i = maxnow+1; i <= n; i++)
16     {
17         for (int plus=i/2;plus>=1;plus--)
18         {
19             dp[i][plus] = dp[i-2*plus][plus]+dp[i][plus+1];
20         }
21     }
22     maxnow = n;
23     printf("%d %u\n", n, dp[n][1]);
24 }
25 
26 void init() {
27     maxnow = 1;
28     for (int i = 0; i < maxn; i++)dp[0][i] = 1;
29     for (int i = 1; i < maxn; i++)
30         for (int j = 0; j < maxn; j++)dp[i][j] = bool(j <= i);
31     while (~scanf("%d", &n) && n) {
32         if (n <= maxnow)printf("%d %u\n",n, dp[n][1]);
33         else
34             dpsolve();
35     }
36 }
37 
38 
39 int main()
40 {
41     init();
42     return 0;
43 }
View Code

解題思路

拆分所需要的數,如8=2+4+2,則在總和為4的回文串的基礎之上,可以在兩端加上2組成一個新的總和為8的回文串,這種情況的種數就是和為4並且最小數大於2的回文串的種數。

下午腦子不太清楚,暈了很長時間,一開始還以為是拆分成兩邊各一個,後來突然發現——我踏馬在寫啥……重寫了兩三次,沒理解清晰題意

B:滑雪

描述Michael喜歡滑雪百這並不奇怪, 因為滑雪的確很刺激。可是為了獲得速度,滑的區域必須向下傾斜,而且當你滑到坡底,你不得不再次走上坡或者等待升降機來載你。Michael想知道載一個區域中最長的滑坡。區域由一個二維數組給出。數組的每個數字代表點的高度。下面是一個例子

 1  2  3  4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9


一個人可以從某個點滑向上下左右相鄰四個點之一,當且僅當高度減小。在上面的例子中,一條可滑行的滑坡為24-17-16-1。當然25-24-23-...-3-2-1更長。事實上,這是最長的一條。輸入輸入的第一行表示區域的行數R和列數C(1 <= R,C <= 100)。下面是R行,每行有C個整數,代表高度h,0<=h<=10000。輸出輸出最長區域的長度。樣例輸入

5 5
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9

樣例輸出

25

來源

Don‘t know

技術分享圖片
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string.h>
 4 #include <algorithm>
 5 #include <stdlib.h>
 6 #include <math.h>
 7 
 8 using namespace std;
 9 
10 const int maxn = 105;
11 int r, c, sum=1, maxlen=1;
12 int dp[maxn][maxn],area[maxn][maxn];
13 int dirr[4] = { 0,0,-1,1 }, dirl[4] = { -1,1,0,0 };
14 
15 struct node {
16     int x, y;
17     int high;
18 }map[maxn*maxn];
19 bool cmp(node&a1,node&a2){
20     return a1.high<a2.high;
21 }
22 
23 void init() {
24     scanf("%d%d", &r, &c);
25     for (int i = 1; i <= r; i++)
26         for (int j = 1; j <= c; j++)
27         {
28             scanf("%d", &map[sum].high);
29             map[sum].x=i,map[sum].y=j;
30             area[i][j]=map[sum].high;
31             dp[i][j]=1;
32             sum++;
33         }
34     sum--;
35     sort(map+1,map+r*c+1,cmp);
36 }
37 
38 void dpsolve(){
39     for(int i=0;i<sum;i++)
40         for(int j=0;j<=3;j++)
41         {
42             if(area[map[i].x+dirr[j]][map[i].y+dirl[j]]>area[map[i].x][map[i].y])
43             {
44                 dp[map[i].x+dirr[j]][map[i].y+dirl[j]]=max(dp[map[i].x+dirr[j]][map[i].y+dirl[j]],dp[map[i].x][map[i].y]+1);
45                 maxlen=max(maxlen,dp[map[i].x+dirr[j]][map[i].y+dirl[j]]);
46             }
47         }
48     printf("%d",maxlen);
49 }
50 
51 int main()
52 {
53     init();
54     dpsolve();
55     return 0;
56 }
View Code

熟悉的題目……把順治換成了Michael

解題思路

將點按高度從小到大排好(我是從小到大劃路線的),然後看四周有沒有點比它高,高的話就把那個點的dp狀態加一( dp[x][y] 指這個點出發的最長路徑)

C:硬幣

描述

宇航員Bob有一天來到火星上,他有收集硬幣的習慣。於是他將火星上所有面值的硬幣都收集起來了,一共有n種,每種只有一個:面值分別為a1,a2… an。 Bob在機場看到了一個特別喜歡的禮物,想買來送給朋友Alice,這個禮物的價格是X元。Bob很想知道為了買這個禮物他的哪些硬幣是必須被使用的,即Bob必須放棄收集好的哪些硬幣種類。飛機場不提供找零,只接受恰好X元。

輸入第一行包含兩個正整數n和x。(1 <= n <= 200, 1 <= x <= 10000)
第二行從小到大為n個正整數a1, a2, a3 … an (1 <= ai <= x)輸出第一行是一個整數,即有多少種硬幣是必須被使用的。
第二行是這些必須使用的硬幣的面值(從小到大排列)。樣例輸入

5 18
1 2 3 5 10

樣例輸出

2
5 10

提示

輸入數據將保證給定面值的硬幣中至少有一種組合能恰好能夠支付X元。
如果不存在必須被使用的硬幣,則第一行輸出0,第二行輸出空行。

技術分享圖片
 1 #include <iostream>
 2 #include <cstdio>
 3 #include <string.h>
 4 #include <algorithm>
 5 #include <stdlib.h>
 6 #include <math.h>
 7 #include <queue>
 8 
 9 using namespace std;
10 
11 const int maxn1 = 205,maxn2=10005;
12 int dp[maxn2],coin[maxn1],f[maxn2];
13 int n,x,sum;
14 queue<int> res;
15 
16 void print() {
17     int p = res.front();
18     printf("%d", coin[p]);
19     res.pop();
20     while (res.empty() != 1) {
21         int p = res.front();
22         printf(" %d", coin[p]);
23         res.pop();
24     } 
25     printf("\n");
26 }
27 
28 void dpsolve() {
29     for(int i=1;i<=n;i++)
30         for (int j = x; j >= coin[i]; j--) {
31             dp[j] += dp[j - coin[i]];
32         }
33     for (int i = 1; i <= n; i++)
34     {
35         memset(f, 0, sizeof(f));
36         f[0] = 1;
37         for (int j = 1; j <= x; j++)
38         {
39             if (coin[i] > j)
40                 f[j] = dp[j];
41             else 
42                 f[j] = dp[j] - f[j - coin[i]];
43         } 
44         if (f[x] == 0) {
45             sum++;
46             res.push(i);
47         }
48     }
49     printf("%d\n", sum);
50     if (sum)
51         print();
52     else
53         printf("\n");
54 }
55 
56 void init() {
57     cin >> n >> x;
58     int c = n,_c=1;
59     while (c--) 
60         cin >> coin[_c++];
61     sort(coin + 1, coin + n + 1);
62     dp[0] = 1;
63 }
64 
65 
66 int main()
67 {
68     init();
69     dpsolve();
70     return 0;
71 }
View Code

我為啥數組又開小了……渾然不知地RE了三次

解題思路

01背包算到達某價錢 x 時的總方案數 dp[x]

然後用方程 f[y]=dp[y]-f[y-coin[x]] 算出不用某硬幣 x 時湊成 y 時的種數(雖然x不同,但我在每個循環裏都是用的 f[y]

f[y] 為0就表示不能不用它~

D:最佳加法表達式

描述

給定n個1到9的數字,要求在數字之間擺放m個加號(加號兩邊必須有數字),使得所得到的加法表達式的值最小,並輸出該值。例如,在1234中擺放1個加號,最好的擺法就是12+34,和為36

輸入有不超過15組數據
每組數據兩行。第一行是整數m,表示有m個加號要放( 0<=m<=50)
第二行是若幹個數字。數字總數n不超過50,且 m <= n-1輸出對每組數據,輸出最小加法表達式的值樣例輸入

2
123456
1
123456
4
12345

樣例輸出

102
579
15

提示

要用到高精度計算,即用數組來存放long long 都裝不下的大整數,並用模擬列豎式的辦法進行大整數的加法。

來源

Guo Wei

技術分享圖片
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <string.h>
  4 #include <algorithm>
  5 #include <stdlib.h>
  6 #include <math.h>
  7 #include <queue>
  8 
  9 using namespace std;
 10 
 11 const int maxn = 60;
 12 int m,countnum=0;//countnum為數字的位數
 13 char num[maxn];
 14 
 15 class bignum {
 16 public:
 17     int longnum[maxn];
 18     int revs[maxn];
 19     int len;
 20     bignum(){
 21         memset(longnum, 0, sizeof(longnum));
 22         memset(revs, 0, sizeof(longnum));
 23         len = 50;
 24     }
 25     bignum(char*a) {
 26         memset(longnum, 0, sizeof(longnum));
 27         memset(revs, 0, sizeof(longnum));
 28         for (int i = 0; a[i] != \0; i++)
 29             longnum[i] = a[i] - 0;
 30         len = strlen(a);
 31         for (int i = 0; i < len; i++)
 32             revs[i] = longnum[len-1 - i];
 33     }
 34     bignum(char*a, char*b) {
 35         memset(longnum, 0, sizeof(longnum));
 36         memset(revs, 0, sizeof(longnum));
 37         for (int i = 0; i <= b - a; i++)
 38             longnum[i] = *(a + i) - 0;
 39         len = b - a + 1;
 40         for (int i = 0; i < len; i++)
 41             revs[i] = longnum[len-1 - i];
 42     }
 43     bignum operator+(bignum x) {
 44         int res[maxn] = { 0 }; char _res[maxn]; int lenres;
 45         for (int i = 0; i < len || i < x.len; i++) {
 46             res[i] += revs[i] + x.revs[i];
 47             lenres = i;
 48             while (res[i] > 9) {
 49                 res[i] -= 10;
 50                 res[i + 1]++;
 51                 lenres = i + 1;
 52             }
 53         }
 54         for (int i = 0; i <= lenres; i++)
 55             _res[i] = res[lenres - i]+0;
 56         return bignum(_res, _res + lenres);
 57     }
 58     bool operator<(bignum&x) {
 59         if (len < x.len)return true;
 60         if (len > x.len)return false;
 61         for (int i = 0; i < len; i++)
 62         {
 63             if (longnum[i] > x.longnum[i])
 64                 return false;
 65             else if(longnum[i] < x.longnum[i]) return true;
 66         }
 67         return true;
 68     }
 69 };
 70 
 71 bignum dp[maxn][maxn];//dp[x][y]為前y個數字裏有x個加號
 72 bignum _num;
 73 
 74 void clear() {
 75     for (int i = 0; i <= m; i++)
 76         for (int j = 1; j <= countnum; j++)
 77             dp[i][j] = bignum();
 78 }
 79 
 80 void dpsolve() {
 81     for(int i=1;i<=m;i++)
 82         for (int j = i + 1; j <= countnum; j++) {
 83             for (int k = i; k < j; k++) {
 84                 bignum res = dp[i - 1][k] + bignum(num + k, num+j - 1);
 85                 if (res < dp[i][j])
 86                     dp[i][j] = res;
 87             }
 88         }
 89 }
 90 
 91 void init() {
 92     while (cin >> m) {
 93         cin >> num;
 94         countnum = strlen(num);
 95         clear();
 96         _num = bignum(num);
 97         for (int i = 1; i <= countnum; i++)
 98             dp[0][i] = bignum(num, num + i - 1);
 99         dpsolve();
100         for (int i = 0; i < dp[m][countnum].len; i++)
101             printf("%d", dp[m][countnum].longnum[i]);
102         printf("\n");
103     }
104 }
105 
106 
107 int main()
108 {
109     init();
110     dpsolve();
111     return 0;
112 }
View Code

我怕不是C++學傻了

滿腦子C++.jpg

解題思路

這個最簡單了,用 dp[x][y] 表示前y個數字加號有x的時候表達式最小值

我裏面有很多浪費計算,不管了

主要是結合了高精度,比較麻煩,很容易錯

WA點:在每個case之間沒有清空dp數組

18.5.24 動規作業