自己模拟,水过三题。
最近做题总是这种,题解一看就懂了的情况。
感觉自己已经不会思考了,五月病。什么都不想做,事情又多,自己又蠢,练习的效果感觉差强人意。
比赛地址:http://acm.csu.edu.cn/OnlineJudge/contest.php?cid=2069
F:
题意:
给三个数n,a,b,且s = (1 << n) = a + b,现在一天可以把s或者s‘对分成一半,以此类推,问第几天能满足分成的两堆一个为a,一个为b。
解析:
我不会说我因为LLwa了几发。。。脑子真是糊死了。
把a和b中小的那个化为二进制数,他二进制为1的最低位就是所要求得时间,因为最低位出现说明之前的位置的数已经被平分过,凑和直接凑就行了。
详见代码。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int maxn = 100 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ncase;
scanf("%d", &ncase);
while (ncase--)
{
int n;
LL a, b;
scanf("%d%lld%lld", &n, &a, &b);
if (a > b)
{
LL t = a;
a = b;
b = t;
}
int cnt = 0;
while (a)
{
if (a & 1)
break;
cnt++;
a >>= 1;
}
printf("%d\n", n - cnt);
}
return 0;
}
K:
因为刚开始卡了F题,丢人,看榜我开始做k,k题G友比这场的时候有叫我看,我当时没有推出来。
然后那天做的时候推啊推的就推出来了。
题意:
有n个pizza,他们是按照卡路里从小到大排的,给一个你想吃的号码k。
现在有三个人,按照卡路里大,卡路里小,你选择的顺序来否决pizza,问你耍点心机,能不能吃到你想吃的号码pizza。
解析:
往有规律的方向想,把1-8的情况列出来,反正是脑子不灵光:
1 YES
1 YES
2 NO
1 NO
2 YES
3 NO
1 NO
2 YES
3 YES
4 NO
1 NO
2 YES
3 YES
4 NO
5 NO
1 NO
2 NO
3 YES
4 YES
5 NO
6 NO
1 NO
2 NO
3 YES
4 YES
5 YES
6 NO
7 NO
1 NO
2 NO
3 YES
4 YES
5 YES
6 NO
7 NO
8 NO
然后去找规律,发现YES的起始刚好是n / 3,YES的个数刚好是 ceil((n - 1) / 3), 所以问题就引刃而解了。
感觉这种方法对于比较蠢的人,such as me,挺好用的!!上次那题dp也是!
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int maxn = 100 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ncase;
int n, p;
while (~scanf("%d%d", &n, &p))
{
for (int i = 0; i < n; i++)
{
int x;
char s[110];
scanf("%d %s", &x, s);
}
int lo = n / 3 + 1;
int hi = lo + ceil((n - 1)/ 3);
// cout << lo << hi << endl;
if (lo <= p && p <= hi)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
A:
做A题的感觉很像上次做哈理工的校赛那道调和级数数学题的感觉,反正题目看不懂。
题意:
求 a[a[i]] = a[i] 的个数。
解析:
刚开始的反应,置换群!然后大脑就空白了。
然后试了下n! 发现-1就行了。黑线。。。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int maxn = 2000000 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
LL pow_mod(LL a, LL n, LL mod)
{
if (n == 0)
return 1;
LL x = pow_mod(a, n >> 1, mod);
LL res = x * x % mod;
if (n % 2)
res = res * a % mod;
return res;
}
LL fun(LL n)
{
LL res = 1;
for(int i = 1; i <= n; i++)
{
res = (res * i) % 1000000007;
}
return res;
}
int main()
{
#ifdef LOCAL
// freopen("in.txt", "r", stdin);
#endif // LOCAL
int ncase;
scanf("%d", &ncase);
while (ncase--)
{
LL n;
scanf("%lld", &n);
printf("%lld\n", fun(n) - 1);
}
return 0;
}
然后是看完题解就懂自己蠢了的E:
题意:
给一串n个数字,这n个数字组成一个圈,然后你来选择起点终点,使得连续的一段是最大的。
即圈圈最大连续字段和。
解析:
加上了一个可以跨越最后一个元素回到首元素的条件,就多上了一种情况。
首先,第一种情况是普通的最大连续字段和,直接解,得一个值;
然后,第二种情况是可以回首元素的字段和,怎么处理呢,很简单,整个段的和,挖空中间一段就模拟了从尾元素回到首元素的情形。
具体实现:先把全段和记录下来,然后把所有元素取负,变为相反数,然后再找一遍,找最大连续字段和最大,即反回去是加回去,然后比较两值大小就行了。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int maxn = 1000000 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
LL a[maxn];
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ncase;
scanf("%d", &ncase);
while (ncase--)
{
memset(a, 0, sizeof(a));
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%lld", &a[i]);
}
LL thisSum = 0;
LL sum1 = 0;
LL ans1 = 0;
for (int i = 0; i < n; i++)
{
thisSum += a[i];
sum1 += a[i];
if (thisSum < 0)
{
thisSum = 0;
}
if (ans1 < thisSum)
{
ans1 = thisSum;
}
a[i] = -a[i];
}
LL sum2 = 0;
LL ans2 = 0;
for (int i = 0; i < n; i++)
{
sum2 += a[i];
if (sum2 < 0)
{
sum2 = 0;
}
if (ans2 < sum2)
{
ans2 = sum2;
}
}
//printf("%lld %lld %lld\n", sum1, ans1, ans2);
printf("%lld\n", max(ans1, sum1 + ans2));
}
return 0;
}
G:
G是二分图最大匹配啊,二分图最大匹配啊,裸的二分图最大匹配啊啊啊!
当时不是学的好好的嘛- -。
题意:
给矩阵n*m上的k个double点,选一个横坐标或者纵坐标来击穿x轴或者y轴,问最小的射击次数。
解析:
最小点覆盖等于二分图最大匹配。
匈牙利算法。
把double的 x 和 y 直接向下取整就ok了。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int maxn = 10000 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
int fr[maxn];
bool vis[maxn];
vector<int> g[maxn];
int n, m, k;
bool match(int v)
{
for (int i = 0; i < g[v].size(); i++)
{
int u = g[v][i];
if (!vis[u])
{
vis[u] = true;
if (fr[u] == -1 || match(fr[u]))
{
fr[u] = v;
return true;
}
}
}
return false;
}
int hungary()
{
int ret = 0;
memset(fr, -1, sizeof(fr));
for (int i = 0; i < k; i++)
{
memset(vis, false, sizeof(vis));
if (match(i))
ret++;
}
return ret;
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ncase;
scanf("%d", &ncase);
while (ncase--)
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 0; i < k; i++)
{
g[i].clear();
}
for (int i = 0; i < k; i++)
{
double x, y;
scanf("%lf%lf", &x, &y);
int X = x;
int Y = y;
g[X].push_back(Y);
}
printf("%d\n", hungary());
}
return 0;
}
C:
新学了一种dp,双调旅行商。
题意:
题意的描述就是双调旅行商的定义啦,这种旅程即为从最左点开始,严格地从左到右直至最右点,然后严格地从右到左直至出发点。
解析:
具体解析放到下一题去讲。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1
using namespace std;
const int maxn = 520;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);
struct Node
{
double x, y;
} node[maxn];
int n;
double dp[maxn][maxn];
bool cmp(Node a, Node b)
{
return a.x < b.x;
}
double dist(Node a, Node b)
{
return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
double DP()
{
dp[1][2] = dist(node[1], node[2]);
for (int j = 3; j <= n; j++)
{
//i < j - 1
for (int i = 1; i < j - 1; i++)
{
dp[i][j] = dp[i][j - 1] + dist(node[j - 1], node[j]);
}
// i = j - 1
dp[j - 1][j] = inf;
for (int k = 1; k < j - 1; k++)
{
double t = dp[k][j - 1] + dist(node[k], node[j]);
if (t < dp[j - 1][j])
{
dp[j - 1][j] = t;
}
}
}
return dp[n - 1][n] + dist(node[n - 1], node[n]);
}
int main()
{
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif // LOCAL
int ncase;
scanf("%d", &ncase);
while (ncase--)
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lf%lf", &node[i].x, &node[i].y);
}
sort(node + 1, node + n + 1, cmp);
printf("%.3lf\n", DP());
}
return 0;
}