题单
P1208 [USACO1.3]混合牛奶 Mixing Milk
思路
这个题贪心显而易见,即先买便宜的,再买贵的,直到满足需求,这样节省开支。,所以时间复杂度为O(min {n, mlogm})
代码
#include <bits/stdc++.h>
using namespace std;
const int maxm = 5005;
int n, m, ans;
struct farmer
{
int p, a;
} f[maxm];
bool cmp (farmer a, farmer b)
{
return a.p < b.p;
}
int main ()
{
scanf ("%d %d", &n, &m);
for (int i = 1; i <= m; i++)
{
scanf ("%d %d", &f[i].p, &f[i].a);
}
sort (f + 1, f + m + 1, cmp);
int p = 1;
while (n > 0)
{
if (n >= f[p].a)
{
n -= f[p].a;
ans += (f[p].a * f[p].p);
}
else
{
ans += (n * f[p].p);
n = 0;
}
p ++;
}
printf ("%d", ans);
return 0;
}
P4995 跳跳!
思路
这个题要求我们的是跳完所有的石柱并使体力的消耗值最大,其中两个石柱之间的高度差越大,体力消耗越多,那么,我们可以对h[i](所有柱子的高度)进行由大到小的排序,使得h[1] > h[2] >……> h[n];那我们肯定首先跳上排完序的第一个柱子以消耗最多的体力,然后跳到第n个柱子,然后跳到第2个柱子,以此类推,我们发现当n等于偶数时,ans = h[1] ^ 2 + (h[1] - h[n]) ^ 2 + (h[2] - h[n]) ^ 2 + (h[2] - h[n - 1]) ^ 2 + …… ,然后我们发现h[1]^2与其他项不太和谐,怎么办呢?观察我们的第3项和第四项,发现我们可以设一个h[n + 1] = 0!这样,当n为偶数的时候,ans = 对(h[i] - h[n + 2 - i]) ^ 2 + (h[i] - h[n + 1 - i]) ^ 2求和,其中i从1到(n / 2)。当n为奇数时,同理,可推出另一个式子,这里不在赘述。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 305;
int n, h[maxn], temp[maxn];
long long ans;
void merge (int l, int r)
{
if (l == r)
{
return;
}
int mid = (l + r) / 2;
merge (l, mid);
merge (mid + 1, r);
int i = l, j = mid + 1, p = l;
while (i <= mid && j <= r)
{
if (h[i] > h[j])
{
temp[p ++] = h[i ++];
}
else
{
temp[p ++] = h[j ++];
}
}
while (i <= mid)
{
temp[p ++] = h[i ++];
}
while (j <= r)
{
temp[p ++] = h[j ++];
}
for (int i = l; i <= r; i++)
{
h[i] = temp[i];
}
}
int main ()
{
scanf ("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf ("%d", &h[i]);
}
merge (1, n);
h[n + 1] = 0;
if (n % 2 == 0)
{
for (int i = 1; i <= n / 2; i++)
{
ans += (h[i] - h[n + 2 - i]) * (h[i] - h[n + 2 - i]) + (h[i] - h[n + 1 - i]) * (h[i] - h[n + 1 - i]);
}
}
else
{
for (int i = 1; i <= (n - 1) / 2; i++)
{
ans += (h[i] - h[n + 2 - i]) * (h[i] - h[n + 2 - i]) + (h[i] - h[n + 1 - i]) * (h[i] - h[n + 1 - i]);
}
int i = (n + 1) / 2;
ans += (h[i] - h[n + 2 - i]) * (h[i] - h[n + 2 - i]);
}
printf ("%lld", ans);
return 0;
}
收获
①这个题推出答案的表达式极大的简化了代码
P2672 [NOIP2015 普及组] 推销员
思路
这个题让我们求的是最多积累的疲劳值,所以我们首先考虑贪心。既然是贪心,那我们一般会对某个变量或多个变量运算形成的整体为对象,进行排序。那么我们这个题是对A[i]还是S[i]进行排序呢?若对S[i]进行由小到大的排序,假设最远的一户人家的下标为a,那么答案就是2 * s[a] + A[a] + A[p1] + A[p2] + ……+A[p(x - 1)],其中A[pi]为A[1]到A[a]中的前(x - 1)大,而每次的前x-1大都不好求,故对s[i]进行排序会使题较难做;若对A[i]进行排序,则只需要计算至多两个s[i]之间的运算,故选择对A[i]进行排序。 那我们对A[i]进行由大到小的排序,当他要访问x户人家时,那么我们可能的答案就是sum (A[i]),i从1到x + max {s[i]},i = 1, 2, ……x。那么有没有可能通过舍弃重新排序后的第x户人家在后面的人家中找一户人家来换取更远的距离来弥补A的减小,并使结果更大?是由可能的,即sum (A[i]),i从1到x + max {s[i] * 2},i = 1, 2, ……x < sum (A[i]),i从1到x-1 + max {2 * s[i] + a[i]},i = x + 1, x + 2, n是由可能成立的 那么我们我们舍弃前x家中的两家,从后面再找两家来使答案有可能吗?若后面的两家中没有一家可以在前x家丢弃一家再找他作为最远家的情况下使答案更优,则显然问题不可能;若后面的两家中有一家可以在前x家丢弃一家再找他,让他作为最远家的情况下使答案更优,那么剩下的一家也只是把前x家中的一个较大的A给替换掉了,所以也不可能。故不可能换两家,更别说两家以上了。 对于sum(A[i]),我们采取前缀和;对于max {s[i] * 2},i = 1, 2, ……x和 max {2 * s[i] + a[i]},i = x + 1, x + 2, ……n,我们用两个数组存储就好了。故O(nlogn)。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000005;
int n;
long long ans, q[maxn], h[maxn], pre[maxn];
struct w
{
long long a, s;
} t[maxn];
bool cmp (w c, w b)
{
return c.a > b.a;
}
int main ()
{
scanf ("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf ("%lld", &t[i].s);
}
for (int i = 1; i <= n; i++)
{
scanf ("%lld", &t[i].a);
}
sort (t + 1, t + n + 1, cmp);
for (int i = 1; i <= n; i++)
{
pre[i] = pre[i - 1] + t[i].a;
}
for (int i = 1; i <= n; i++)
{
q[i] = max (q[i - 1], 2 * t[i].s);
}
for (int i = n; i >= 1; i--)
{
h[i] = max (h[i + 1], 2 * t[i].s + t[i].a);
}
for (int i = 1; i <= n; i++)
{
printf ("%lld\n", max (pre[i] + q[i], pre[i - 1] + h[i]));//对于 pre[i - 1] + h[i]可能会有一点疑问,那就是如果h[i]代表后面那家不够远怎么办?这个不用多想,因为这种情况下,他肯定小于pre[i] + q[i],肯定不会成为答案。
}
return 0;
}
收获
①这个题的思路真的很值得反复考虑、复习
P1199 [NOIP2010 普及组] 三国游戏
思路
我和计算机都是为了胜利这一目标,故与博弈论有点相似
但是这个题的题解真的太难写了,直接丢个写的比较好的题解吧
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
int a[maxn][maxn], n;
bool cmp (int x, int y)
{
return x > y;
}
int main ()
{
scanf ("%d", &n);
for (int i = 1; i < n; i++)
{
for (int j = i + 1; j <= n; j++)
{
scanf ("%d", &a[i][j]);
a[j][i] = a[i][j];
}
}
int ans = -2147483647;
for (int i = 1; i <= n; i++)
{
sort (a[i] + 1, a[i] + n + 1, cmp);
ans = max (ans, a[i][2]);
}
printf ("1\n%d", ans);
return 0;
}
P1094 [NOIP2007 普及组] 纪念品分组
思路
根据要求(最小的分组数目)可知,我们应尽量充分利用每组纪念品价格之和的上限w,即两个物品的价格之和应尽量接近w。首先,我们对价格进行由小到大的排序,那我们肯定不会让小的两个进行配对,因为两个小的配对,可能会浪费w;那我们也不会让两个大的进行配对,因为即使价格之和小于w,当分组达到一定地步时,就会出现两个小的进行配对的情况,也会浪费w。那么我们选择一个大的和一个小的进行配对,首先肯定是最大的和最小的进行配对,如果他俩之和<=w,在他俩在一组;否则,最大的单独在一组,因为第二小的与最大的之和也会>w,剩下的以此类推即可,直到分完组。
代码
#include <bits/stdc++.h>
using namespace std;
int w, n, p, ans;
priority_queue <int, vector <int>, greater <int> > qi;
priority_queue <int, vector <int>, less <int> > qii;
int main ()
{
scanf ("%d %d", &w, &n);
for (int i = 1; i <= n; i++)
{
scanf ("%d", &p);
qi.push (p);
qii.push (p);
}
while (n > 0)
{
if (qi.top() + qii.top() > w)
{
ans ++, n --;
qii.pop();
}
else
{
ans ++, n-= 2;
qi.pop(), qii.pop();
}
if (n == 0)
{
break;
}
}
printf ("%d", ans);
return 0;
}
收获
①重新学习了优先队列