A - Too Rich
HDU - 5527
题意:现在有需要的金额p,每种面值所拥有的数量。现在要用尽可能多的纸币来组成p。
思路:贪心 + DFS
比赛后期读了一下题还以为是类似于多重背包什么的…
现在要用尽可能多的纸币来组成p,一开始正着暴搜搜T了…
这里其实应该反过来想,理解为:用尽量少的纸币凑成sum - p ( sum是所有纸币的面额之和 ),用纸币总数num - ans就是答案。而这部分用尽量少的纸币凑sum-p就是很普通的贪心,尽量先选面额大的,用DFS搜索即可,但是这里应该注意一个问题是:题目给出的这些面额1,20,50,100,200,500,1000,2000里,除了<20,50>和<200,500>这两个关系以外,每个面值都是比它大的所有面值的因子,也就是说,要选尽量少的纸币,那么面值大的能拿就拿走,否则还要用小面值的来凑它,比如能拿50就拿50,否则以后还要拿5个10凑成50。而对于那两个特殊的关系,20不是50的因子,200不是500的因子,但是20却是50+50的因子,200却是500+500的因子,那么就会出现奇偶性质的讨论,这意味着拿50和500的时候,还要考虑拿少一张的情况,给20或200的纸币留空间,否则将会漏掉情况,如20,20,20,50凑60块,20,20,20,50,50凑110块等 ,都需要这样多搜一层来处理。DFS的时候同时记录用最多和少用一个的情况往下递归,这样就避免了奇偶性造成的错误。
#include
#include
#include
#include
#include
#include
#include
B - Count a * b
HDU - 5528
积性函数 推导
C - Play a game
HDU - 5529
D - Pipes selection
HDU - 5530
E - Rebuild
HDU - 5531
题意:按顺序给出一些点,并将相邻(1和2,2和3,……,n和1)的两点连线,现在对于每两个相邻点组成的线段,都要以点为圆心,构造出两个相切的圆。对于给出的所有点,若能构造出来,输出所有圆的面积之和,并按输入顺序输出这些圆的半径;若无法构造出来,则输出IMPOSSIBLE
思路:三分查找
二分:适用于单调函数(不一定严格单调)中逼近求解某点的值
三分:凹性函数或凸性函数求凹/凸点
如图所示,已知左右端点L、R,要求找到凸点的位置。
思路:通过不断缩小 [L,R] 的范围,无限逼近凸点。
做法:先取 [L,R] 的中点 mid,再取 [mid,R] 的中点 mmid,通过比较 f(mid) 与 f(mmid) 的大小来缩小范围。
当最后 L = R-1 时,再比较下这两个点的值,我们就找到了答案。
1. 当 f(mid) > f(mmid) 的时候,我们可以断定 mmid 一定在凸点的右边。
反证法:假设 mmid 在凸点的左边,则 mid 也一定在凸点的左边,又由 f(mid) > f(mmid) 可推出 mmid < mid,与已知矛盾,故假设不成立。
所以,此时可以将 R = mmid 来缩小范围。
2. 当 f(mid) < f(mmid) 的时候,我们可以断定 mid 一定在凸点的左边。
反证法:假设 mid 在凸点的右边,则 mmid 也一定在凸点的右边,又由 f(mid) < f(mmid) 可推出 mid > mmid,与已知矛盾,故假设不成立。
同理,此时可以将 L = mid 来缩小范围。int SanFen(int l, int r) { while(l < r-1) { int mid = (l+r)/2; //与二分法类似,先取整个区间的中间值mid int mmid = (mid+r)/2; //再取右侧区间的中间值mmid,从而把区间分为三个小区间 if( f(mid) > f(mmid) ) //若mid比mmid更靠近最值,舍弃右区间 r = mmid; else //否则舍弃左区间 l = mid; } return f(l) > f(r) ? l : r; }
另一种三分写法
const double eps = 1e-10; double Ternary_Search(double low, double up) { double m1,m2; while(up - low >= eps) { m1 = low + (up - low) / 3; m2 = up - (up - low) / 3; if(f(m1) <= f(m2)) low = m1; else up = m2; } return (m1+m2)/2; }
当构成的多边形边数为奇数时,可列下式:
可得:
这样可以直接算得r1,再由 得到其它的半径,注意,若其中有半径 < 0, 可以直接判断为IMPOSSIBLE
当构成的多边形边数为偶数时:
上述式子恰好能把两边的消掉,但是依旧可以得到一个等式
首先我们可以判断这个式子是否成立,若不成立则直接判断为IMPOSSIBLE。
由于我们最后要求的面积是一个凹性函数,故我们可以用三分法 去查找,第一步是要确定三分的左右边界left和right,通过推导我们可以列出如下式子:
通过这样的计算来不断约束左右边界left、right
然后做三分查找即可
注意
- #define eps 1e-8
PI = acos(-1.0)
AC代码:
#include
#include
#include
#include
#include
#include
#include
F - Almost Sorted Array
HDU - 5532
题意:给出一个序列,判断删除一个数字,能否构成不上升或者不下降的序列
思路:LIS
AC代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn], n;
bool solve() {
int pos[2] = {-1, -1};
for(int i = 1; i < n; i++) {
if(a[i] < a[i - 1]) {
pos[0] = i - 1;
pos[1] = I;
break;
}
}
if(pos[0] == -1)
return true;
int prev = 0, cnt = 0;
for(int i = 0; i < n; i++) {
if(pos[0] != i) {
if(prev == 0)
prev = a[i];
else {
if(a[i] < prev) {
cnt++;
break;
}
else
prev = a[i];
}
}
}
prev = 0;
for(int i = 0; i < n; i++) {
if(pos[1] != i) {
if(prev == 0)
prev = a[i];
else {
if(a[i] < prev) {
cnt++;
break;
}
else
prev = a[i];
}
}
}
if(cnt < 2)
return true;
else
return false;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0; i < n; I++)
scanf("%d", &a[I]);
if(solve())
printf("YES\n");
else {
for(int i = 0; i < n / 2; I++)
swap(a[i], a[n - i - 1]);
if(solve())
printf("YES\n");
else
printf("NO\n");
}
}
return 0;
}
G - Dancing Stars on Me
HDU - 5533
题意:给出n个坐标(x,y)(x,y是正整数),问由这些点构成的图形是否是正n边形。
思路:由于坐标都是正整数,故只有正方形有可能构成。计算四个边长是否相等且对角线相等即可。
AC代码:
#include
#include
#include
#include
#include
#include
H - Partial Tree
HDU - 5534
题意:给n个点,现在要构造出一棵树(n-1个点),现在给出度数为1、2、3、……、n-1的点权f(i),现在要让这个树的点权之和尽量的大,并将其输出。
思路:完全背包
I - Chess Puzzle
HDU - 5535
J - Chip Factory
HDU - 5536
题意:给出一个数列,求
思路:直接暴
AC代码:
#include
using namespace std;
const int MAX = 1e3 + 5;
int n, s[MAX];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin >> T;
while(T--) {
cin >> n;
for(int i = 0; i < n; I++)
cin >> s[i];
int ans = 0;
for(int i = 0; i < n; I++)
for(int j = i + 1; j < n; j++)
for(int k = 0; k < n; k++)
if(k != i && k != j)
ans = max(ans, ((s[i] + s[j]) ^ s[k]));
cout << ans << endl;
}
}
K - Maximum Spanning Forest
HDU - 5537
L - House Building
HDU - 5538
题意:给出一个n*m的格子,每个格子里的数字就是立方体的高度。求用多少瓷砖能把这些立方体都盖住。
思路:求表面积(不算底部)。
AC代码:
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 55;
int a[maxn][maxn];
int turn[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
int main() {
int T;
int n, m;
scanf("%d", &T);
while(T--) {
scanf("%d%d", &n, &m);
memset(a, 0, sizeof a);
int ans = 0;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
scanf("%d", &a[i][j]);
if(a[i][j]) ++ans;
}
}
int ii, jj;
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
for(int k = 0; k < 4; ++k) {
ii = i + turn[k][0];
jj = j + turn[k][1];
if(a[i][j] > a[ii][jj]) {
ans += a[i][j] - a[ii][jj];
}
}
}
}
printf("%d\n", ans);
}
return 0;
}
M - Security Corporation
HDU - 5539