这次终于涨分了,比赛的时候思路也比前几次清晰多了,继续加油。
A题题意:A每天可以获得ai个糖,他会把糖给B,但是一天顶多给8颗,问最少要几天B获得的糖数不少于k颗。
WA了几次,一开始题意理解错了。。遇到水题还是得冷静分析分析。
最直接的思路就是如果A手上的糖数多于8颗,就给B8颗,如果少于8颗,就全部给他。
直接贴代码。
#include<iostream>
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
int num[105];
for (int i = 1;i <= n;i++)
{
cin >> num[i];
}
int cnt = 0;
int temp = 0;
int ans = 0;
while (temp<k&&ans<n)
{
if (cnt + num[++ans] > 8)
{
temp += 8;
cnt = cnt + num[ans] - 8;
}
else
{
temp += cnt + num[ans];
cnt = 0;
}
}
if (temp < k)cout << -1 << endl;
else cout << ans << endl;
return 0;
}
B题题意:有k种部队,每个部队有ai个人,他们要坐飞机,飞机座位有n行,每行8个座位,1和2,3和4,4和5,5和6,7和8连在一起。不同部队的人不能坐在连在一起的位置,请问能不能有没有方案安排他们坐好。
一开始以为是大力if,但是看完claris精短的代码后,顿时豁然开朗。。
思路:相邻的座位其实分成3种,1个位置,2个位置,和4个位置。我们只要判断是否能把一个部队的人塞进这几种位置中的一个,如果都不行,那就NO,如果可以,该位置减一。
另外,如果可能的话,最好把一个部队的放进4个位置当中,这样影响的座位最少,可以有更多的不同的部队坐下去。
感觉像脑筋急转弯。。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 10005;
int a[maxn];
int c[5];
int main()
{
int n, k;
scanf("%d%d", &n, &k);
int x;
memset(c, 0, sizeof(c));
for (int i = 1;i <= k;i++)
{
scanf("%d", &x);
a[x]++;
}
c[2] = n * 2;
c[4] = n;
int i,j;
for ( i = 10000;i > 0;i--)
{
while (a[i]--)
{
for ( j = 4;j;j--)if(c[j])
{
c[j]--;
int t = min(i, j);
a[i - t]++;
if (j - i - 1 > 0)c[j - i - 1]++;
break;
}
if (!j)
{
puts("NO");
return 0;
}
}
}
puts("YES");
return 0;
}
C题题意:求一颗树从结点1上到每个叶子深度的期望。
WA了几次,原因没有仔细思考到每个叶子的概率,以为到每个叶子的概率是一样的,然而不是。
思路很简单,深搜广搜都可以,维护每个结点的深度和到该结点的概率即可。
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 100005;
vector<int>V[maxn];
int state[maxn];
bool vis[maxn];
double q[maxn];
queue<int>Q;
void bfs()
{
Q.push(1);
vis[1] = 1;
state[1] = 0;
q[1] = 1;
double ans = 0;
//int cnt = 0;
//int length = 0;
while (!Q.empty())
{
int u = Q.front();Q.pop();
int Size = V[u].size();
if (u != 1 && Size == 1)
{
ans += q[u] * state[u];
}
else
{
int v;
int Count;
if (u == 1)Count = Size;
else Count = Size - 1;
for (int i = 0;i <Size; i++)if (vis[V[u][i]] == 0)
{
v = V[u][i];
vis[v] = 1;
state[v] = state[u] + 1;
q[v] = q[u] / (double)(Count);
Q.push(v);
}
}
}
printf("%.15f\n", ans);
}
int main()
{
int n;
scanf("%d", &n);
if (n == 1) { printf(" 0.000000000000000\n");return 0; }
int u, v;
for (int i = 0;i < n - 1;i++)
{
scanf("%d%d", &u, &v);
V[u].push_back(v);
V[v].push_back(u);
}
bfs();
return 0;
}
D题题意:给定一个序列,要求求出该序列所有gcd不为1的序列(不要求连续),并定义该序列的值为序列元素个数*gcd(该序列的所有元素),求序列值的总和。
思路:首先我们可以求出以i为公约数的序列长度总和。再减去其中最大公约数不为i的序列长度。比如2 4 6 ,以2为公约数序列有2,4,6,2 4,2 6,4 6,2 4 6,但是其中4,6,两个序列的最大公约数并不为2(为其本身),所以要减掉这两个。因为小的包括大的,所以应该从后往前推。具体实现见代码。
代码:
#include<iostream>
#include<cstring>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 1000005;
const int mod = 1000000007;
int a[maxn];
int t[maxn];
int f[maxn];
void init()
{
t[0] = 1;
for (int i = 1;i <= 1000000;i++)
t[i] = t[i - 1] * 2 % mod;
}
int main()
{
init();
int n;
cin >> n;
int x;
memset(a, 0, sizeof(a));
for (int i = 1;i <= n;i++)
{
scanf("%d", &x);
a[x]++;
}
int ans = 0;
for (int i = 1000000;i > 1;i--)
{
x = 0;
for (int j = i;j <= 1000000;j += i)x += a[j];
if (x)
{
f[i] = (1LL*x*t[x - 1]) % mod;
for (int j = i + i;j <= 1000000;j += i)
f[i] = (1LL*f[i] - f[j]+mod) % mod;
ans = (ans + 1LL * f[i] * i) % mod;
}
}
printf("%d\n", ans);
return 0;
}
这里有几个问题:1,为什么要预处理一个2的幂?2,为什么f[i]可以这么算?留给读者思考。
E题题意:一个墙壁连着两个城堡,总共有k油漆可以涂在城堡上,墙壁的硬度等于连着的两个城堡涂得油漆量相乘,现在给出城堡之间的连通关系,问如何分配油漆使得总硬度最大。
这道题的思路不太好说,直接说结论,结论就是求出这个图的最大团,然后将k油漆均衡分配给最大团中的城堡,这样会使得总硬度最大。
我想给出严谨的证明,但我的潜意识觉得不需要,因为我们只是在做题而已,而题内部的玄妙其实不用管太多,只需要找到其中包含的规律即可。
话不多说,开始找规律。
如果只有两个城堡相连,那么硬度值为k^2/4, 那么如果有3个城堡相连呢,我们学过均值不等式,显然硬度值取最大值时,k是均匀分配给这三个城堡的,这时候硬度值为k^2/3。 这个值是比前者大的,如果不止三个城堡呢,在前者(三个城堡相连)的基础上再加上2个相连的城堡,我们可以试试分配看,可以发现,不管怎么分配,都会比k^2/3小,这是我们可以推广到很多城堡:倘若有n个城堡相互相连(专业术语称为最大团),那么我们可以将k均衡分配给这n个城堡,得出来的硬度值为:(n-1)/2n *k^2,必然是最大的,其他的城堡分配的油漆则为0。
这样巧妙的思考完后,那么问题来了,如何求一个图上的最大团呢,实际上这是一个NP问题,还没有很好的方式来解决这个问题,但基于这个题的数据量(点的个数最多为40),我们其实只需要爆搜(笑)即可。
一个简单的DFS即可。
代码如下:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
#pragma warning (disable:4996)
const int maxn = 45;
int a[maxn][maxn];
vector<int>V;
int ans = -1;
int n, k;
bool vis[maxn];
bool check(int idx)
{
int Size = V.size();
for (int i = 0;i < Size;i++)
if (a[idx][V[i]] == 0)
return 0;
return 1;
}
void dfs(int idx)
{
if (idx > n)
return;
if (ans >=(int) (V.size() + n - idx ))
return;
bool flag = false;
for(int i=idx+1;i<=n;i++)
if (check(i))
{
flag = true;
V.push_back(i);
//vis[i] = 1;
dfs(i);
//vis[i] = 0;
V.pop_back();
}
if (!flag)
{
int Size = V.size();
ans = max(ans, Size);
}
}
int main()
{
cin >> n >> k;
for (int i = 1;i <= n;i++)
for (int j = 1;j <= n;j++)
scanf("%d", &a[i][j]);
dfs(0);
if (ans == -1)
{
printf("%.12f\n", (double)k *k / 4.0);
}
else
printf("%.12f\n", (double)k*k * (ans - 1) / (double)(2 * ans));
return 0;
}
信心的读者可以发现,dfs里面有个巧妙的剪枝,.
if (ans >=(int) (V.size() + n - idx ))return
为什么可以这样剪呢,留给读者思考~~