Noip 模拟练习6
- 满分300,本人110。修正后260。
- 难度中等。
赌徒
Description
有 N 个赌徒,手里各自有自己的筹码。在赌博时,他们能向周围的赌徒借钱,所以他们
的筹码可能是负的,但筹码都一定是整数。当结束赌博时,N 个赌徒当中,筹码刚是另外 3 个
人筹码的总和的赌徒为胜者。如果有多个符合条件的赌徒,选择筹码最大的那个人为胜者。
例如 5 个赌徒,结束赌博时各有 2,3,5,7,12,则他们中的胜者是持有 12 的人,因为 12=2+3+7。
Input
第一行为一个整数 n(1 <= n <= 1000 )表示有 n 个赌徒。接下去 n 行,每行一
个整数 x(10 ^ 8 <= x <= 10 ^ 8 )表示赌博结束时,每个赌徒手中的筹码。
Output
仅一个整数表示赌博结束时,胜者手中的筹码数。如果没有胜者则输出“no
solution”。
Sample Input
5
2
3
5
7
12
Sample output
12
题解:
- 暴力枚举。
- emmm…..我这题爆零了。
- 一开始写了dp,写着写着发现不对,马上改成了记搜。信心满满地交上去,结果爆了。原因是有一个边界情况没考虑到。
- 结果正解就是3重循环 + 剪枝。惊了。
- 具体就是先排序。再第一重枚举胜者的钱,第二重枚举蒻者1的钱,第三重枚举蒻者2的钱,没必要枚举蒻者3的钱了,因为可以通过前面枚举的算出来,假设算出来的蒻者4的钱为x。这时如果用桶判x是否在数组中肯定爆空间。那么直接二分查找呗。
这样写的复杂度是O(n ^ 3),过不了。来点剪枝吧!当你枚举蒻者1的钱时,假设当前蒻者1的钱 * 3都 < 胜者的钱。那么直接break。理由很简单,蒻者中最强的人的3倍都达不到,那剩下的组合怎么可能达得到?
- 总结:数据小的可以考虑下搜索。但像这种,只有3个人这种数据极小的。可以考虑暴力 + 剪枝。
不要把任何问题都想得那么难。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 1005
using namespace std;
int n;
int a[N];
int find(int x)
{
int pos = lower_bound(a + 1, a + 1 + n, x) - a;
return a[pos];
}
int main()
{
cin >> n;
if(n < 4) {cout << "no solution"; return 0;}
for(int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n);
for(int i = n; i >= 4; i--)
for(int j = i - 1; j >= 1; j--)
{
if(a[j] * 3 < a[i]) break;
for(int k = j - 1; k >= 1; k--)
{
if(a[k] * 2 + a[j] < a[i]) break;
int v1 = a[i], v2 = a[j], v3 = a[k], v4 = v1 - v2 - v3;
if(find(v4) == v4) {cout << v1; return 0;}
}
}
cout << "no solution";
return 0;
}
猴子吃香蕉
Description
一些研究者正在研究猴子的 IQ,他们在天花板上悬挂了一串香蕉,并且提供了一些箱
子,如果猴子十分聪明的话,它会把这些箱子叠加在一起,直到拿到香蕉为止。研究者一共
提供了 n 种箱子,而且每种箱子的数量不限。每种箱子都是长方体,尺寸为(xi,yi,zi)。
在叠加箱子的时候,要求摆在上方的箱子的底面的长和宽一定要小于摆在下方的一个箱子
的顶面的长和宽。(箱子只能一个一个地叠加)
给定一些箱子的尺寸,编程求出它们能叠加在一起的最大高度。
Input
第一行为 n(1 <= n <= 30 ),表示箱子的种数。
接下来 n 行,每行 3 个数字 xi,yi,zi,分别表示箱子的尺寸。
1 <= xi, yi, zi <= 104 。
Output
- 仅一个数字,表示它们能叠加在一起的最大高度。
Sample Input
2
6 8 10
5 5 5
Sample output
21
题解:
- dp。
直接A掉了。但刚拆包看到这题的数据范围居然 <= 30时惊了,还以为是状压。结果仔细思考,其实本质就是“导弹拦截”,于是迅速切掉。(这题由于数据范围太小搜索也可以过… …
由于一个箱子可以颠来倒去,所以一个箱子其实是“6”个箱子。假设有x, y, z。x做高有2种,y做高有2种,z做高有2种,共6种。然后将n * 6种箱子加入进数组。对这个数组做一遍LIS即可。但注意维护的是最大值而不是最长上升/下降序列。
#include <iostream>
#include <cstdio>
#include <algorithm>
#define N 5005
using namespace std;
struct A {int x, y, z;} a[N];
int n, ans, cnt;
int dp[N];
bool cmp(A u, A v)
{
if(u.x == v.x)
{
if(u.y == v.y) return u.z > v.z;
else return u.y > v.y;
}
else return u.x > v.x;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i++)
{
int x, y, z;
cin >> x >> y >> z;
a[++cnt].x = x, a[cnt].y = y, a[cnt].z = z;
a[++cnt].x = y, a[cnt].y = x, a[cnt].z = z;
a[++cnt].x = y, a[cnt].y = z, a[cnt].z = x;
a[++cnt].x = z, a[cnt].y = y, a[cnt].z = x;
a[++cnt].x = z, a[cnt].y = x, a[cnt].z = y;
a[++cnt].x = x, a[cnt].y = z, a[cnt].z = y;
}
n *= 6;
sort(a + 1, a + 1 + n, cmp);
for(int i = 1; i <= n; i++) dp[i] = a[i].z;
for(int i = 2; i <= n; i++)
for(int j = 1; j < i; j++)
if(a[j].x > a[i].x && a[j].y > a[i].y)
dp[i] = max(dp[i], dp[j] + a[i].z);
for(int i = 1; i <= n; i++) ans = max(ans, dp[i]);
cout << ans;
return 0;
}
项链工厂
题目:
- 有图,转链接
题解:
- 平衡树/线段树
- 这题正解我就先咕咕咕了,
将来填上。 - 先把暴力打好,第一次交错竟是因为R操作写错了,十分可惜。
- 这题的暴力思路很简单,但是许多细节!
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 500005
using namespace std;
int n, c, q;
int a[N], b[N];
bool vis[N];
int read()
{
int x = 0; char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
return x;
}
void step1()
{
int k = read() % n;
for(int i = 1; i <= n; i++)
{
int j = (i + k) % n;
if(!j) j = n;
b[j] = a[i];
}
for(int i = 1; i <= n; i++) a[i] = b[i];
}
void step2()
{
for(int i = 2; i <= (n + 1) / 2; i++)
swap(a[i], a[n - i + 2]);
}
void step3()
{
int u = read(), v = read();
swap(a[u], a[v]);
}
int getCnt(int u, int v)
{
int pos = u, cnt = 0;
while(pos != v)
{
cnt++, pos++;
if(pos == n + 1) pos = 1;
}
return cnt;
}
void step4()
{
int u = read(), v = read(), w = read();
int pos = u, cnt = 1 + getCnt(u, v);
for(int i = 1; i <= cnt; i++)
{
a[pos] = w;
pos++;
if(pos == n + 1) pos = 1;
}
}
void step5()
{
int ans = 0, pos;
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= n; i++)
if(!vis[i])
{
ans++, vis[i] = 1;
pos = i;
while(1)
{
pos++;
if(pos == n + 1) pos = 1;
if(vis[pos]) break;
if(a[pos] == a[i]) vis[pos] = 1;
else break;
}
pos = i;
while(1)
{
pos--;
if(!pos) pos = n;
if(vis[pos]) break;
if(a[pos] == a[i]) vis[pos] = 1;
else break;
}
}
printf("%d\n", ans);
}
void step6()
{
int l = read(), r = read();
int ans = 0, u = l, v = -1, cnt = 1 + getCnt(l, r);
for(int i = 1; i < cnt; i++)
{
v = (u + 1) % n;
if(!v) v = n;
if(a[u] != a[v]) ans++;
u++;
if(u == n + 1) u = 1;
}
printf("%d\n", ans + 1);
}
int main()
{
cin >> n >> c;
for(int i = 1; i <= n; i++) a[i] = read();
cin >> q;
for(int i = 1; i <= q; i++)
{
char c[4]; scanf("%s", c);
if(c[0] == 'R') step1();
else if(c[0] == 'F') step2();
else if(c[0] == 'S') step3();
else if(c[0] == 'P') step4();
else if(c[0] == 'C' && c[1] == 'S') step6();
else if(c[0] == 'C') step5();
}
return 0;
}