A - Too Rich
题意:现在有需要的金额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 <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 15;
int tp[maxn] = {1,5,10,20,50,100,200,500,1000,2000};
int cost[maxn];
int ans, p;
void dfs(int now, int num, int remain) {
if(remain == 0) {
//cout << "in" << endl;
ans = min(ans, num);
return;
}
if(remain < 0 || now < 0)
return;
int cnt = min(remain/tp[now], cost[now]);
//cout << cnt << endl;
dfs(now - 1, num + cnt, remain - cnt*tp[now]);
if(cnt >= 1) {
cnt -= 1;
dfs(now - 1, num + cnt, remain - cnt*tp[now]);
}
return;
}
int main() {
int T, _num, sum, num;
scanf("%d", &T);
while(T--) {
scanf("%d", &p);
sum = 0, ans = INF, num = 0;
for(int i = 0; i < 10; ++i) {
scanf("%d", &_num);
cost[i] = _num;
num += _num;
sum += _num * tp[i];
}
if(sum < p) {
printf("-1\n");
continue;
}
dfs(9, 0, sum-p);
if(ans == INF)
printf("-1\n");
else
printf("%d\n", num - ans);
}
return 0;
}
B - Count a * b
HDU - 5528
积性函数 推导
C - Play a game
D - Pipes selection
E - Rebuild
题意:按顺序给出一些点,并将相邻(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 <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
const int INF = 0x3f3f3f3f;
#define PI acos(-1.0)
#define eps 1e-8
using namespace std;
const int maxn = 1e4+5;
struct point {
double x, y;
}p[maxn];
double d[maxn], r[maxn];
int n;
double f_(double x) {
r[0] = x;
double area = r[0] * r[0];
for(int i = 1; i < n; ++i) {
r[i] = d[i-1] - r[i-1];
area += r[i] * r[i];
}
return area;
}
double sanfen(double l, double r) {
double mid, mmid;
while(r - l > eps) {
mid = (l+r)/2.0;
mmid = (r+mid)/2.0;
if(f_(mid) - f_(mmid) <= eps) {
r = mmid;
}
else {
l = mid;
}
}
return (l+r)/2.0;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
scanf("%lf%lf", &p[i].x, &p[i].y);
if(i > 0)
d[i-1] = sqrt((p[i].x-p[i-1].x)*(p[i].x-p[i-1].x) + (p[i].y-p[i-1].y)*(p[i].y-p[i-1].y));
}
d[n-1] = sqrt((p[n-1].x-p[0].x)*(p[n-1].x-p[0].x) + (p[n-1].y-p[0].y)*(p[n-1].y-p[0].y));
if(n&1) {
double fz = 0;
for(int i = 0; i < n; ++i) {
//printf("%d -> %f\n", i, d[i]);
if(i&1) fz -= d[i];
else fz += d[i];
}
double area = 0;
r[0] = fz / 2.0;
if(r[0] < 0) {
puts("IMPOSSIBLE");
continue;
}
area += r[0] * r[0];
bool flag = true;
for(int i = 1; i < n; ++i) {
r[i] = d[i-1] - r[i-1];
//printf("%f\n", r[i]);
if(r[i] < 0) {
flag = false;
break;
}
area += r[i] * r[i];
}
if(!flag) {
puts("IMPOSSIBLE");
continue;
}
printf("%.2f\n", area*PI);
for(int i = 0; i < n; ++i) {
printf("%.2f\n", r[i]);
}
}
else {
double judge = 0;
double left = 0, right = (double)INF;
for(int i = 0; i < n; ++i) {
//printf("**%.2f\n", d[i]);
if(i&1) {
judge -= d[i];
left = max(left, judge);
}
else {
judge += d[i];
right = min(right, judge);
}
}
//printf("** %.2f %.2f\n", left, right);
//printf("** %.2f\n", judge);
if(judge - 0 > eps || left - right > eps) {
puts("IMPOSSIBLE");
continue;
}
//cout << "in" << endl;
r[0] = sanfen(left, right);
//cout << "out" << endl;
printf("%.2f\n", f_(r[0])*PI);
for(int i = 0; i < n; ++i) {
printf("%.2f\n", r[i]);
}
}
}
return 0;
}
F - Almost Sorted Array
题意:给出一个序列,判断删除一个数字,能否构成不上升或者不下降的序列
思路:LIS
AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
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
题意:给出n个坐标(x,y)(x,y是正整数),问由这些点构成的图形是否是正n边形。
思路:由于坐标都是正整数,故只有正方形有可能构成。计算四个边长是否相等且对角线相等即可。
AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <map>
using namespace std;
const int maxn = 5;
typedef long long ll;
struct point {
int x, y;
}p[maxn];
int main() {
int T, n;
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0; i < n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
}
if(n != 4) {
printf("NO\n");
continue;
}
map<int, int> mp;
for(int i = 0; i < n; ++i) {
for(int j = i+1; j < n; ++j) {
int d = (p[i].x-p[j].x)*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y);
++mp[d];
}
}
if(mp.size() == 2) {
bool flag = true;
map<int, int>::iterator it = mp.begin();
for( ;it != mp.end(); ++it) {
if(it->second != 2 && it->second != 4) {
flag = false;
break;
}
}
if(flag) {
printf("YES\n");
}
else {
printf("NO\n");
}
}
else {
printf("NO\n");
}
}
return 0;
}
H - Partial Tree
题意:给n个点,现在要构造出一棵树(n-1个点),现在给出度数为1、2、3、……、n-1的点权f(i),现在要让这个树的点权之和尽量的大,并将其输出。
思路:完全背包
I - Chess Puzzle
J - Chip Factory
题意:给出一个数列,求
思路:直接暴
AC代码:
#include <iostream>
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
L - House Building
HDU - 5538
题意:给出一个n*m的格子,每个格子里的数字就是立方体的高度。求用多少瓷砖能把这些立方体都盖住。
思路:求表面积(不算底部)。
AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
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;
}
网友评论