HDU100题简要题解(2060~2069)

这十题感觉是100题内相对较为麻烦的,好多数学题,有点搞我心态...歪比巴卜

HDU2060 Snooker

题目链接

Problem Description
background:
Philip likes to play the QQ game of Snooker when he wants a relax, though he was just a little vegetable-bird. Maybe you hadn't played that game yet, no matter, I'll introduce the rule for you first.
There are 21 object balls on board, including 15 red balls and 6 color balls: yellow, green, brown, blue, pink, black.
The player should use a white main ball to make the object balls roll into the hole, the sum of the ball's fixed value he made in the hole is the player's score. The player should firstly made a red ball into the hole, after that he gains red-ball's value(1 points), then he gets the chance to make a color ball, then alternately. The color ball should be took out until all the red-ball are in the hole. In other word, if there are only color balls left on board, the player should hit the object balls in this order: yellow(2 point), green(3 point), brown(4 point), blue(5 point), pink(6 point), black(7 point), after the ball being hit into the hole, they are not get out of the hole, after no ball left on board, the game ends, the player who has
the higher score wins the game. PS: red object balls never get out of the hole.
I just illustrate the rules that maybe used, if you want to contact more details, visit http://sports.tom.com/snooker/ after
the contest.
for example, if there are 12 red balls on board(if there are still red ball left on board, it can be sure that all the color
balls must be on board either). So suppose Philp can continuesly hit the ball into the hole, he can get the maximun score is
12 * 1 (12 red-ball in one shoot) + 7 * 12(after hit a red ball, a black ball which was the most valuable ball should be the target) + 2 + 3 + 4 + 5 + 6 + 7(when no red ball left, make all the color ball in hole).
Now, your task is to judge whether Philip should make the decision to give up when telling you the condition on board(How many object balls still left not in the hole and the other player's score). If Philp still gets the chance to win, just print "Yes", otherwise print "No". (PS: if the max score he could get on board add his current score is equal to the opponent's current score, still output "Yes")

Input
The first line contains a numble N indicating the total conditions. Then followed by N lines, each line is made of three integers:
Ball_Left P_Score O_Score represeting the ball number left on board, Philp's current score, and the opponent's current score.
All the input value are in 32 bit integer value range.

Output
You should caculate the max score left Philp can gain, and judge whether he has the possiblity to win.

Sample Input
2
12 1 1
1 30 39

Sample Output
Yes
No

看到题目,脸上笑嘻嘻,心里......硬着头皮看完了,发现是道水题:)
分两种情况,加起来:
1、大于六个球也就是有红球的情况,每打一个红球就紧接着打一个黑球,打完红球再把其他颜色的打进去
2、少于六个球也就是无红球的情况,假装剩下的都是相对较大的球

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n, x, num1, num2;

int main() {
  while (scanf("%d", &n) != EOF) {
    while (n--) {
      scanf("%d%d%d", &x, &num1, &num2);
      if (x > 6) 
        num1 += (x - 6) * 8 + 27;
      else {
        int cnt = 7;
        while (x--) {
  	  num1 += cnt;
  	  cnt--;
        }
      }
      if (num1 >= num2) printf("Yes\n");
      else printf("No\n");
    }
  }
  return 0;
}

HDU2061 Treasure the new start, freshmen!

题目链接

Problem Description
background:
A new semester comes , and the HDU also meets its 50th birthday. No matter what's your major, the only thing I want to tell you is:"Treasure the college life and seize the time." Most people thought that the college life should be colorful, less presure.But in actual, the college life is also busy and rough. If you want to master the knowledge learned from the book, a great deal of leisure time should be spend on individual study and practise, especially on the latter one. I think the every one of you should take the learning attitude just as you have in senior school.
"No pain, No Gain", HDU also has scholarship, who can win it? That's mainly rely on the GPA(grade-point average) of the student had got. Now, I gonna tell you the rule, and your task is to program to caculate the GPA.
If there are K(K > 0) courses, the i-th course has the credit Ci, your score Si, then the result GPA is
GPA = (C1 * S1 + C2 * S2 +……+Ci * Si……) / (C1 + C2 + ……+ Ci……) (1 <= i <= K, Ci != 0)
If there is a 0 <= Si < 60, The GPA is always not existed.

Input
The first number N indicate that there are N test cases(N <= 50). In each case, there is a number K (the total courses number), then K lines followed, each line would obey the format: Course-Name (Length <= 30) , Credits(<= 10), Score(<= 100).
Notice: There is no blank in the Course Name. All the Inputs are legal

Output
Output the GPA of each case as discribed above, if the GPA is not existed, ouput:"Sorry!", else just output the GPA value which is rounded to the 2 digits after the decimal point. There is a blank line between two test cases.

Sample Input
2
3
Algorithm 3 97
DataStruct 3 90
softwareProject 4 85
2
Database 4 59
English 4 81

Sample Output
90.10

Sorry!

头大,看完发现又是一道水题,题意的精华在于其中的公式:
GPA = (C1 * S1 + C2 * S2 +……+Ci * Si……) / (C1 + C2 + ……+ Ci……) (1 <= i <= K, Ci != 0)
然后用这个算,并且用一个flag记录是否存在有不及格科目的情况

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n, m;
double a[101], b[101];

int main() {
  while (scanf("%d", &n) != EOF) {
    while (n--) {
      scanf("%d", &m);
      double sum = 0;
      double num = 0;
      int flag = 0;
      for (int i = 1; i <= m; i++) {
  	string s;
  	cin >> s;
  	scanf("%lf%lf", &a[i], &b[i]);
  	if (b[i] < 60) flag = 1;
  	  sum += a[i] * b[i];
  	  num += a[i];
  	}
  	if (flag == 1) cout << "Sorry!" << endl;
  	else {
  	  double ans = (sum + 0.0) / num;
  	  printf("%.2lf\n", ans);
  	}
  	if (n) cout << endl;
    }
  }
  return 0;
}

HDU2062 Subset sequence

题目链接

Problem Description
Consider the aggregate An= { 1, 2, …, n }. For example, A1={1}, A3={1,2,3}. A subset sequence is defined as a array of a non-empty subset. Sort all the subset sequece of An in lexicography order. Your task is to find the m-th one.

Input
The input contains several test cases. Each test case consists of two numbers n and m ( 0< n<= 20, 0< m<= the total number of the subset sequence of An ).

Output
For each test case, you should output the m-th subset sequence of An in one line.

Sample Input
1 1
2 1
2 2
2 3
2 4
3 10

Sample Output
1
1
1 2
2
2 1
2 3 1

人傻了,康了题解才解决这题,卡这题上了一上午...这题我感觉算是这100题里思维含量最高的几个了,反正我是傻了(因为菜
借这题终于明白了三元运算符,高中的时候看学弟用过,一脸懵逼也没想着去学,终于知道是个啥东西了(求gcd的时候还挺方便的,直接一行解决)

题目大意:考虑一个集合 An = { 1, 2, ..., n}。比如,A1={1},A3={1,2,3}。我们称一个非空子集元素的排列为一个子集序列。对所有的子序列按字典顺序排序。你的任务就是给出第m个子序列。

显然不可能记录全部的子集,忒大了
先来看看n=2、n=3的情况

1、A2={1,2}
子序列为:
{1} {1,2}
{2} {2,1}
2、A3={1,2,3}
子序列为:
{1} {1,2} {1,2,3} {1,3} {1,3,2}
{2} {2,1} {2,1,3} {2,3}

可以发现,An可以按照首数字分成n组,每一组的个数为An-1的序列个数多一个,使用f[n]表示An的序列的个数
则可以得到递推式:f[n] = n * (f[n - 1] + 1)
我们拿最后一组数据 3 10 举例详细分析一下
易知第10个序列在第二组里,所以第一个数字是2,由于10 - (2 - 1) * 5 = 5,所以它在第2组的第5个,把2输出,原来的数组里删除2,变成1、3两个数
之后还有一个判断,由5 - 1 = 4 ≠ 0,表明第10个序列不是第2组的首序列,而每一组只有首序列为一个元素的序列,所以2后面还有数字
此时,剩下了数字1和3,可以组成四个序列,道理和A2={1,2}相同,一定程度上可以相当于A2={1,3}
所以和A2={1,2}道理相同,剩下的序列又可以分成两组,每组2个序列
4在第2组,剩下的数组中,第二个元素是3,所以输出3,重复之前操作把数组里的3删除,剩下1
然后继续套用之前的式子4 - (2 - 1) * 2 = 2,即它是第2组的第2个
判断一下,2 - 1 = 1 ≠ 0,表示2不是首序列,它的后面还有别的数字
......
重复上述操作,直到n = 0或后面为空集为止
最后输出每个序列里的第1个元素,就得到2 3 1

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

long long m, c[21];
int n, t, s[21];

//c[i]代表n=i时,每组的数量,例如c[2]代表n=2时每组有2个 
void init() {
  for (int i = 1; i < 21; i++)
    c[i] = c[i - 1] * (i - 1) + 1;
  return;
}

int main() {
  init();
  while (scanf("%d%lld", &n, &m) != EOF) {
    for (int i = 0; i < 21; i++) s[i] = i;
      while (n > 0 && m > 0) {
        t = m / c[n] + (m % c[n] > 0 ? 1 : 0);
        if (t > 0) {
          printf("%d", s[t]);
          for (int i = t; i <= n; i++) s[i] = s[i + 1];
          m -= ((t - 1) * c[n] + 1);
          putchar(m == 0 ? '\n' : ' ');
        }
        n--;
      }
  }
  return 0;
}

HDU2063 过山车

题目链接

Problem Description
RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?

Input
输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000
1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。

Output
对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。

Sample Input
6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0

Sample Output
3

经典的二分图劈配,用到匈牙利算法
然鹅我忘了...找了点文章现学,这里附上一篇讲解匈牙利算法的文章 趣写算法系列之匈牙利算法
配图海星吧,比较好玩,虽然没有某大佬课件上的好玩(笑
定义a[u][v]储存两点之间的连线情况,girl[i]表示第i个男生与女生的配对情况,vis[i]表示第i个男生有没有与女生配对
从一个女生开始,扫描另一边(男生部分)与之相连的点,已经有女人的男生不予考虑
如果发现了一个男生与当前女生有边相连并且这个男生目前没有其他女人,于是vis[i] = 1
如果第i个男生之前没有女人的话,那么马上将他们配成一对,并且返回1
还有一种情况,就是该男生之前有女人,那么我们就策划让其以前配对的女生另外找一个男生,这不难实现,再次调用这个函数即可
也就是

if (girl[i] == 0 || find(girl[i])) {
  girl[i] = x;
  return 1;
}

得到代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 501;
int k, n, m, x, y;
int vis[maxn], girl[maxn], a[maxn][maxn];

bool find(int x) {
  for (int i = 1; i <= n; i++) {
    if (a[x][i] && vis[i] == 0) {
      vis[i] = 1;
      if (girl[i] == 0 || find(girl[i])) {
        girl[i] = x;
  	return 1;
      }
    }
  }
  return 0;
}

int main() {
  while (scanf("%d", &k) != EOF) {
    if (k == 0) break;
    scanf("%d%d", &m, &n);
    memset(a, 0, sizeof(a));
    memset(girl, 0, sizeof(girl));
    for (int i = 1; i <= k; i++) {
      scanf("%d%d", &x, &y);
      a[x][y] = 1;
    }
    int cnt = 0;
    for (int i = 1; i <= m; i++) {
      memset(vis, 0, sizeof(vis));
      if (find(i)) cnt++;
    }
    printf("%d\n", cnt);
  }
  return 0;
}

HDU2064 汉诺塔III

题目链接

Problem Description
约19世纪末,在欧州的商店中出售一种智力玩具,在一块铜板上有三根杆,最左边的杆上自上而下、由小到大顺序串着由64个圆盘构成的塔。目的是将最左边杆上的盘全部移到右边的杆上,条件是一次只能移动一个盘,且不允许大盘放在小盘的上面。
现在我们改变游戏的玩法,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到下盘的上面。
Daisy已经做过原来的汉诺塔问题和汉诺塔II,但碰到这个问题时,她想了很久都不能解决,现在请你帮助她。现在有N个圆盘,她至少多少次移动才能把这些圆盘从最左边移到最右边?

Input
包含多组数据,每次输入一个N值(1<=N=35)。

Output
对于每组数据,输出移动最小的次数。

Sample Input
1
3
12

Sample Output
2
26
531440

十分十分十分经典的递推题,之前第一次学的时候人傻了,简单讲一下
我们假设把n层塔从A经过B挪到C需要f[n]步,挪动的步骤我们可以分解为:将上层的n-1层从A经过B挪到C要f[n-1]步,将第n层从A挪到B需要一步,再将之前的n-1层从C经B挪到A需要f[n-1]步,将第n层从B挪到C需要一步,最后将n-1层从A经B挪到C需要f[n-1]步。所以得到递推关系式:f[n] = 3 * f[n - 1] + 2

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int n;
long long dp[36];

int main() {
  dp[1] = 2;
  for (int i = 2; i <= 35; i++) dp[i] = dp[i - 1] * 3 + 2;
  while (scanf("%d", &n) != EOF) printf("%lld\n", dp[n]);
  return 0;
}

HDU2065 "红色病毒"问题

题目链接

Problem Description
医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶,腺嘧啶均是成对出现的。
现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.

Input
每组输入的第一行是一个整数T,表示测试实例的个数,下面是T行数据,每行一个整数N(1<=N<2^64),当T=0时结束.

Output
对于每个测试实例,输出字符串个数的最后两位,每组输出后跟一个空行.

Sample Input
4
1
4
20
11
3
14
24
6
0

Sample Output
Case 1: 2
Case 2: 72
Case 3: 32
Case 4: 0

Case 1: 56
Case 2: 72
Case 3: 56

康了康题,毫无头绪,康了眼题解:指数型母函数+泰勒级数展开
???什么东西,听都没听说过,没办法,搜了一堆文章现学,算是一知半解了,只知道这题的思路,再给我一题肯定还是做不出来...
比较容易理解的一篇文章
我自己再简单说一下(式子太难打了,直接嫖的大佬打好的QwQ):
求A B C D 在规定条件下n个元素的排列个数,先写出指数型母函数
G(X) = ( 1 + x + x^2/2! + x^3/3! +... )^2 * ( 1+ x + x^2/2! + x^4/4! + .. )^2
前者表示:B, D出现方式不限制;后者表示:A, C 只能出现偶数或者不出现情况
又知:
ex=1+x/1!+x2/2!+x^3/3!+...
e(-x)=1-x/1!+x2/2!-x^3/3!+...
化简得:

G(x) = e^(2x) * ((e^x+e^(-x))/2)^2
     = (1/4) * e^(2x) * (e^(2x) + 2 + e^(-2x))
     = (1/4) * (e^(4x) + 2*e^(2x) +1)
     = (1/4) * ((1+4x/1!+(4x)^2/2!+(4x)^3/3!+...+(4x)^n/n!) + 2*(1+2x/1!+(2x)^2/2!+(2x)^3/3!+...+(2x)^n/n!)+1) 

得: x^n 项系数

a(n) = (1/4) * ((4x)^n/n! + 2*(2x)^n/n!)
     = (1/4) * ( 4^n*x^n/n! + 2^(n+1)*x^n/n!)
     = (4^(n-1) + 2^(n-1)) * x^n/n!

即所求 F(n) = (4^(n-1) + 2^(n-1)) % 100

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

int t, ans;
long long n;

int cal(int a, long long b) {
  int num = 1;
  while (b) {
    if (b & 1) num = (num * a) % 100;
    a = (a * a) % 100;
    b = b / 2;
  }
  return num;
}

int main() {
  while (scanf("%d", &t) != EOF) {
    if (t == 0) break;
    for (int i = 1; i <= t; i++) {
      scanf("%lld", &n);
      printf("Case %d: ", i);
      if (n == 0) printf("1\n");
      else {
  	ans = (cal(2, n - 1) + cal(4, n - 1)) % 100;
  	printf("%d\n", ans);
      }
    }
    printf("\n");
  }
  return 0;
} 

HDU2066 一个人的旅行

题目链接

Problem Description
虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,0),很多事,还能丰富自己的阅历,还可以看美丽的风景……草儿想去很多地方,她想要去东京铁塔看夜景,去威尼斯看电影,去阳明山上看海芋,去纽约纯粹看雪景,去巴黎喝咖啡写信,去北京探望孟姜女……眼看寒假就快到了,这么一大段时间,可不能浪费啊,一定要给自己好好的放个假,可是也不能荒废了训练啊,所以草儿决定在要在最短的时间去一个自己想去的地方!因为草儿的家在一个小镇上,没有火车经过,所以她只能去邻近的城市坐火车(好可怜啊~)。

Input
输入数据有多组,每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个,草儿想去的地方有D个;
接着有T行,每行有三个整数a,b,time,表示a,b城市之间的车程是time小时;(1=<(a,b)<=1000;a,b 之间可能有多条路)
接着的第T+1行有S个数,表示和草儿家相连的城市;
接着的第T+2行有D个数,表示草儿想去地方。

Output
输出草儿能去某个喜欢的城市的最短时间。

Sample Input
6 2 3
1 3 5
1 4 7
2 8 12
3 8 4
4 9 12
9 10 2
1 2
8 9 10

Sample Output
9

直接裸的dijkstra,将草儿的家看做0,从草儿家到相邻镇的花费看做0,求草儿家到各个目的地的最短路

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;

int t, s, d;
int map[1001][1001];
int dis[1001], vis[1001];

void init() {
  for (int i = 0; i <= 1000; i++) {
    map[i][i] = 0;
    for (int j = i + 1; j <= 1000; j++)
      map[i][j] = map[j][i] = INF;
  }
}

void dij() {
  int minn, cnt;
  for (int i = 0; i <= 1000; i++) {
    vis[i] = 0;
    dis[i] = map[0][i];
  }
  vis[0] = 1;
  for (int i = 0; i <= 1000; i++) {
    minn = INF;
    for (int j = 0; j <= 1000; j++) 
      if (vis[j] == 0 && dis[j] < minn) {
  	minn = dis[j];
  	cnt = j;
    }
    vis[cnt] = 1;
    for (int j = 0; j <= 1000; j++)
      if (vis[j] == 0) 
        dis[j] = min(dis[j], dis[cnt] + map[cnt][j]);
  }
}

int main() {
  while (scanf("%d%d%d", &t, &s, &d) != EOF) {
    int u, v, w;
    init();
    for (int i = 1; i <= t; i++) {
      scanf("%d%d%d", &u, &v, &w);
      if (w < map[u][v]) map[u][v] = map[v][u] = w;
    }
    for (int i = 1; i <= s; i++) {
      int ss;
      scanf("%d", &ss);
      map[ss][0] = map[0][ss] = 0;
    }
    dij();
    int ans = INF;
    for (int i = 1; i <= d; i++) {
      int se;
      scanf("%d", &se);
      ans = min(ans, dis[se]);
    }
    printf("%d\n", ans);
  }
  return 0;
}

HDU2067 小兔的棋盘

题目链接

Problem Description
小兔的叔叔从外面旅游回来给她带来了一个礼物,小兔高兴地跑回自己的房间,拆开一看是一个棋盘,小兔有所失望。不过没过几天发现了棋盘的好玩之处。从起点(0,0)走到终点(n,n)的最短路径数是C(2n,n),现在小兔又想如果不穿越对角线(但可接触对角线上的格点),这样的路径数有多少?小兔想了很长时间都没想出来,现在想请你帮助小兔解决这个问题,对于你来说应该不难吧!

Input
每次输入一个数n(1<=n<=35),当n等于-1时结束输入。

Output
对于每个输入数据输出路径数,具体格式看Sample。

Sample Input
1
3
12
-1

Sample Output
1 1 2
2 3 10
3 12 416024

传说中的卡特兰数,如下图,沿着对角线的那一串数字(画图画了20分钟...

得到代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;

long long dp[36][36];
int n, cnt;

int main() {
  for (int i = 1; i <= 36; i++) {
    dp[i][0] = 1;
    dp[0][i] = 1;
  }
  for (int i = 1; i < 36; i++)
    for (int j = 1; j < 36; j++)
      if (i == j) dp[i][j] = dp[i][j - 1];
      else dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
  while (scanf("%d", &n) != EOF) {
    if (n == -1) break;
    cnt++;
    cout << cnt << " " << n << " ";
    printf("%lld\n", 2 * dp[n][n]);
  }
  return 0;
}

HDU2068 RPG的错排

题目链接

Problem Description
今年暑假杭电ACM集训队第一次组成女生队,其中有一队叫RPG,但做为集训队成员之一的野骆驼竟然不知道RPG三个人具体是谁谁。RPG给他机会让他猜猜,第一次猜:R是公主,P是草儿,G是月野兔;第二次猜:R是草儿,P是月野兔,G是公主;第三次猜:R是草儿,P是公主,G是月野兔;......可怜的野骆驼第六次终于把RPG分清楚了。由于RPG的带动,做ACM的女生越来越多,我们的野骆驼想都知道她们,可现在有N多人,他要猜的次数可就多了,为了不为难野骆驼,女生们只要求他答对一半或以上就算过关,请问有多少组答案能使他顺利过关。

Input
输入的数据里有多个case,每个case包括一个n,代表有几个女生,(n<=25), n = 0输入结束。

Sample Input
1
2
0

Sample Output
1
1

核心公式:ans[i] = (i - 1) * (ans[i - 1] + ans[i - 2])
关于为什么是这个公式在HDU2048已经讲过了,不明白的话移步 HDU100题简要题解(2040~2049)
n个人要猜对一半以上才可以,也就是只能错一半以下,错排
而对于这些错的人,也可以互不相同,所以还要用到组合数

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;

int n;
LL ans[26], a[26][26];

void init() {
  memset(ans, 0, sizeof(ans));
  memset(a, 0, sizeof(a));
  ans[0] = 1;
  ans[1] = 0;
  ans[2] = 1;
  ans[3] = 2;
  for (LL i = 4; i < 26; i++)
    ans[i] = (i - 1) * (ans[i - 1] + ans[i - 2]);
  for (int i = 0; i < 26; i++) a[i][0] = 1;
  for (int i = 1; i < 26; i++)
    for (int j = 1; j <= i; j++)
      a[i][j] = a[i - 1][j] + a[i - 1][j - 1];
}

int main() {
  init();
  while (scanf("%d", &n) != EOF) {
    if (n == 0) break;
    long long sum = 0;
    for (int i = 0; i <= n / 2; i++)
      sum += ans[i] * a[n][i];
    printf("%lld\n", sum);
  }
  return 0;
}

HDU2069 Coin Change

题目链接

Problem Description
Suppose there are 5 types of coins: 50-cent, 25-cent, 10-cent, 5-cent, and 1-cent. We want to make changes with these coins for a given amount of money.
For example, if we have 11 cents, then we can make changes with one 10-cent coin and one 1-cent coin, or two 5-cent coins and one 1-cent coin, or one 5-cent coin and six 1-cent coins, or eleven 1-cent coins. So there are four ways of making changes for 11 cents with the above coins. Note that we count that there is one way of making change for zero cent.
Write a program to find the total number of different ways of making changes for any amount of money in cents. Your program should be able to handle up to 100 coins.

Input
The input file contains any number of lines, each one consisting of a number ( ≤250 ) for the amount of money in cents.

Output
For each input line, output a line containing the number of different ways of making changes with the above 5 types of coins.

Sample Input
11
26

Sample Output
4
13

单纯的dp,讲解穿插在代码里了比较方便

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;

int n;
int a[5] = {1, 5, 10, 25, 50};
LL dp[251][251];

int main() {
  while (scanf("%d", &n) != EOF) {
    memset(dp, 0, sizeof(dp));
    dp[0][0] = 1;
    for (int i = 0; i < 5; i++)    //用的硬币
      for (int j = 1; j <= 100; j++)     //硬币的数目
  	for (int k = a[i]; k <= n; k++)
  	  dp[k][j] += dp[k - a[i]][j - 1];     //用j个硬币组成k值
    int ans = 0;
    for (int i = 0; i <= 100; i++) ans += dp[n][i];
    printf("%d\n", ans);
  }
  return 0;
}
posted @ 2020-11-21 10:48  Moominn  阅读(664)  评论(0编辑  收藏  举报