上述优化后的算法把对一个区间的操作转化为左、右两个端点上的操作,再通过前缀和得到原问题的解。这种思想很常用,我们在后面还会多次遇到。该算法的时间复杂度为O(N+M)。
另外,在本题的数据中,一条关系(A,B)可能会输入多次,要注意检查,对于重复的关系,只在第一次出现时执行相关操作即可。这一点需注意
#include
using namespace std;
map<pair<int, int>, bool> existed;
int c[10010], d[10010];
int main() {
int n, p, h, m;
cin >> n >> p >> h >> m;
for (int i = 1; i <= m; i++) {
int a, b;
scanf("%d%d", &a, &b);
if (a > b) swap(a, b);
if (existed[make_pair(a, b)]) continue;
d[a + 1]--;
d[b]++;
existed[make_pair(a, b)] = true;
}
for (int i = 1; i <= n; i++) {
c[i] = c[i - 1] + d[i];
printf("%d\n", h + c[i]);
}
cout << endl;
}
凡人追妹传之大学篇
可以插入到中间,左边和右边,也有可能无法插入
分别进行判断即可
我就想着不算难吧,但是就只有几个人写了
#include
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int cb[100000];
int cc[100000][2];
int main() {
int n;
while (cin >> n) {
for (int i = 0; i < n; i++) {
cin >> cb[i];
}
sort(cb, cb + n);
if (n == 1) {
cout << "-1\n";
} else if (n == 2) {
int c = cb[1] - cb[0];
if (c & 1) {
cout << "2\n";
cout << cb[0] - c << " " << cb[1] + c << "\n";
} else if (cb[0] == cb[1]) {
cout << "1\n";
cout << cb[0] << "\n";
} else {
cout << "3\n";
cout << cb[0] - c << " " << cb[0] + c / 2 << " " << cb[1] + c << "\n";
}
} else if (n >= 3) {
int pd = 0;
int min = INF;
for (int i = 1; i < n; i++) {
if (cb[i] - cb[i - 1] < min) {
min = cb[i] - cb[i - 1];
}
}
for (int i = 1; i < n; i++) {
if ((cb[i] - cb[i - 1]) != min && (cb[i] - cb[i - 1]) == min * 2) {
cc[pd][0] = cb[i];
cc[pd++][1] = cb[i - 1];
} else if ((cb[i] - cb[i - 1]) != min) {
pd = -1;
break;
}
}
int q = 1;
for (int i = 1; i < n; i++) {
if (cb[i] != cb[i - 1]) {
q = 0;
}
}
if (q) {
cout << "1\n";
cout << cb[0] << "\n";
} else {
if (pd > 1 || pd == -1) {
cout << "0\n";
} else if (pd == 1) {
cout << "1\n";
cout << (cc[0][0] + cc[0][1]) / 2 << "\n";
} else if (pd == 0) {
int c = cb[1] - cb[0];
cout << "2\n";
cout << cb[0] - c << " " << cb[n - 1] + c << "\n";
}
}
}
}
}
北宋大烟篓
经典约瑟夫环问题
如果看不懂代码的可以看这里
https://blog.csdn.net/u011500062/article/details/72855826
#include
#include
using namespace std;
int cir(int n, int m) {
int p = 0;
for (int i = 2; i <= n; i++) {
p = (p + m) % i;
}
return p + 1;
}
int main() {
int t;
int n, m;
cin >> t;
for (int i = 0; i < t; i++) {
cin >> n >> m;
cout << cir(n, m) << "\n";
}
}
游戏败家子
动态规划
假设数组 cost 的长度为 n,则 n个阶梯分别对应下标 0 到 n-1,楼层顶部对应下标 n,问题等价于计算达到下标 n 的最小花费。可以通过动态规划求解。
创建长度为 n+1 的数组dp,其中dp[i] 表示达到下标 i的最小花费。
由于可以选择下标 0或 1 作为初始阶梯,因此有 dp[0]=dp[1]=0。
当 2≤i≤n 时,可以从下标 i−1 使用 cost[i−1] 的花费达到下标 i,或者从下标 i-2使用 cost[i−2] 的花费达到下标 i。为了使总花费最小,dp[i] 应取上述两项的最小值,因此状态转移方程如下:
dp[i]=min(dp[i−1]+cost[i−1],dp[i−2]+cost[i−2])
依次计算 dp 中的每一项的值,最终得到的dp[n]即为达到楼层顶部的最小花费。
#include
#include
#include
#include
int minCostClimbingStairs(int *cost, int costSize) {
int dp[costSize];
dp[0] = cost[0];
dp[1] = cost[1];
for (int i = 2; i < costSize; i++) {
dp[i] = fmin(dp[i - 1], dp[i - 2]) + cost[i];
}
return fmin(dp[costSize - 1], dp[costSize - 2]);
}
int main() {
int cost[1000];
int costSize = 0;
int n;
while (~scanf("%d", &n)) {
while (n--) {
scanf("%d", &costSize);
for (int i = 0; i < costSize; i++) {
scanf("%d", &cost[i]);
}
printf("%d", minCostClimbingStairs(cost, costSize));
}
}
}
超神排位赛
模拟题,灵感来自某年山东省省赛,有一道炉石的排位机制模拟
按照题意模拟即可,只是细节要注意,例如升段的时候,降段位的时候,王者50星,王者不会掉到星耀等几种情况
#include
using namespace std;
char r[10][20] = {
"", "Bronze", "Silver", "Gold", "Platinum", "Diamond", "Starry", "Master", "Challenger"};
int limit[] = {
0, 3, 3, 4, 5, 5, 5, 999, 999};
int limit2[] = {
0, 3, 4, 4, 5, 5, 5, 999, 999};
int rule[] = {
0, 3, 3, 3, 4, 5, 6, 999, 999};
char br[6][10] = {
"", "one", "two", "three", "four", "five"};
int cnt, counter;
char c1[20], c2[20];
int c, n;
void ascertain() {
for (int i = 0; i < 9; i++) {
if (strcmp(c1, r[i]) == 0) {
cnt = i;
break;
}
}
for (int i = 0; i < 6; i++) {
if (strcmp(c2, br[i]) == 0) {
counter = i;
break;
}
}
}
void verify() {
if (c > limit[cnt] && cnt != 7) {
c -= limit[cnt];
counter--;
if (counter == 0) {
cnt++;
counter = limit2[cnt];
}
}
}
int main() {
int cb[1000];
while (cin >> c1 >> c2 >> c) {
ascertain();
cin >> n;
int plus = 0;
for (int i = 0; i < n; i++) {
cin >> cb[i];
if (cb[i] == 1) {
plus++;
} else {
plus = 0;
}
if (plus == rule[cnt] && cb[i] == 1) {
c += 2;
verify();
plus = 0;
} else if (cb[i] == 1) {
c++;
verify();
} else if (cb[i] == -1) {
if (cnt == 1 && counter == 3 && c == 0 || cnt == 7 && c == 0) {
continue;
}
c--;
if (c == -1) {
counter++;
if (counter == limit2[cnt] + 1) {
counter = 1;
cnt--;
}
c = limit[cnt] - 1;
}
}
}
if (cnt == 7 && c < 50) {
cout << r[cnt] << " " << c << "\n";
} else if (cnt == 7 && c >= 50) {
cout << r[8] << "\n";
} else {
cout << r[cnt] << " " << br[counter] << " " << c << "\n";
}
}
}
这个大师太过正经
简单快速幂
按照一般的暴力过不了
#include
typedef long long ll;
int main() {
ll t;
scanf("%lld", &t);
while (t--) {
ll n, a, b, p = 1000003;
ll res = 0;
scanf("%lld", &n);
for (ll i = 0; i < n; i++) {
ll q = 1;
scanf("%lld %lld", &a, &b);
while (b) {
if (b & 1)q = q * a % p;
b >>= 1;
a = a * a % p;
}
res = (res + q) % p;
}
printf("%lld\n", res % p);
}
return 0;
}
糖果的四十六亿种分法
题意:有n个盒子组成一个圆,盒子里总共有不超过n个蛋糕,有的有好几个,有的为0。可以将一个盒子里的蛋糕往左右两个盒子里移动,一次只能移动一个,使最终每个盒子里有不超过一个蛋糕(可以没有),求最小的移动数
思路:把盒子分为两种,有蛋糕和没蛋糕的,然后建图,对于有蛋糕的盒子拆点,拆为蛋糕数-1个点,因为它本身留一个蛋糕不移动。需要注意的是因为是个圆,所以移动的路径肯定不会大于n/2,大于n/2时判断一下就好了。
#include
#include
#include
#include
#include
using namespace std;
const int N = 510;
const int INF = 0x3f3f3f3f;
int n, nx, ny;
int lx[N], ly[N], slack[N], match[N], s[N][N];
bool visx[N], visy[N];
int v[N], tmp[N];
bool hungary(int v) {
visx[v] = true;
for (int i = 0; i < ny; i++) {
if (visy[i]) continue;
if (lx[v] + ly[i] == s[v][i]) {
visy[i] = true;
if (match[i] == -1 || hungary(match[i])) {
match[i] = v;
return true;
}
} else slack[i] = min(slack[i], lx[v] + ly[i] - s[v][i]);
}
return false;
}
void km() {
memset(match, -1, sizeof match);
memset(ly, 0, sizeof ly);
for (int i = 0; i < nx; i++)
lx[i] = -INF;
for (int i = 0; i < nx; i++)
for (int j = 0; j < ny; j++)
lx[i] = max(lx[i], s[i][j]);
for (int i = 0; i < nx; i++) {
memset(slack, 0x3f, sizeof slack);
while (true) {
memset(visx, 0, sizeof visx);
memset(visy, 0, sizeof visy);
if (hungary(i)) break;
else {
int d = INF;
for (int j = 0; j < ny; j++)
if (!visy[j]) d = min(d, slack[j]);
for (int j = 0; j < nx; j++)
if (visx[j]) lx[j] -= d;
for (int j = 0; j < ny; j++)
if (visy[j]) ly[j] += d;
else slack[j] -= d;
}
}
}
}
int main() {
int n;
int a;
cin >> a;
while (a--) {
cin >> n;
int t = n / 2;
for (int i = 0; i < n; i++)
scanf("%d", v + i);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
s[i][j] = -INF;
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < n; i++)
if (v[i] == 0)
tmp[i] = cnt2++;
for (int i = 0; i < n; i++)
for (int j = 0; j < v[i] - 1; j++) {
for (int k = 0; k < n; k++) {
if (v[k] == 0) {
if (abs(k - i) >= t)
s[cnt1][tmp[k]] = -(n - abs(k - i));
else
s[cnt1][tmp[k]] = -abs(k - i);
}
}
cnt1++;
}
nx = cnt1, ny = cnt2;
km();
int res = 0;
for (int i = 0; i < ny; i++)
if (match[i] != -1)
res += s[match[i]][i];
printf("%d\n", -res);
}
return 0;
}
我有一座研究院
过题人数最多的一道题
三种方法:暴力,贪心,公式
暴力
如果哪边的轻就加哪边,直到一边数量已经到达一半
#include
#include
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int a = 0, b = 0, n, ta = 0, tb = 0;
cin >> n;
for (int i = n; i > 0; i--) {
if (ta == n / 2) {
b += pow(2, i);
tb++;
} else if (tb == n / 2) {
a += pow(2, i);
ta++;
} else if (a <= b) {
a += pow(2, i);
ta++;
} else if (a > b) {
b += pow(2, i);
tb++;
}
}
cout << abs(a - b) << "\n";
}
return 0;
}
贪心
最后一个和前n-1个是一组,其他是一组,因为要保持两边的绝对值最小
#include
#include
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int a = 0, b = 0, n;
cin >> n;
a += pow(2, n);
for (int i = 1; i < n / 2; i++) {
a += pow(2, i);
}
for (int i = n / 2; i < n; i++) {
b += pow(2, i);
}
cout << abs(a - b) << "\n";
}
return 0;
}
公式
我们可以得出规律,按照规律推导结果
#include
#include
using namespace std;
int main() {
int t, n;
cin >> t;
while (t--) {
cin >> n;
cout << pow(2, n / 2 + 1) - 2 << "\n";
}
return 0;
}
特拉福珠宝俱乐部
题意:
求树上每个点能走出的最远距离
思路:
树形DP
程序中的数据结构:
A数组记录V结点沿子树方向最长路所经过V的孩子编号。
B数组记录沿子树方向的次长路长度。
dp1数组 记录子树方向的最长路。
dp2数组记录来自父节点的最长路。
vectorcnt:记录每条边的信息。
我们假设一个结点V的孩子为{v1,v2,v3……vn};
-
如果vi在V沿子树方向的最长路上
那么dis【vi】 = max(dp1【vi】,max(dp2【V】,B【V】) + dis(V,vi))
说一下内嵌的max,以图中2(V)结点2结点来自父节点的最长路和2结点的子树方向次长路的较大值加上V 和vi的距离
-
如果vi不在V沿子树方向的最长路上
那么dis【vi】= max(dp1【V】,dp2【V】) + dis(V, vi);
最长路要么来自父节点子树方向的最长路加上dis(V,vi),要不来自父节点V的从V的父节点来的最长路 + dis(V, vi);
对于根节点特殊处理因为没有父节点只有次最长路,并且单孩子的结点没有次长路。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int INF = 510;
int n, A[INF], B[INF], dp1[INF],dp2[INF];
typedef pair<int, int> PLL;
vector<PLL>cnt[INF];
int dfs1(int root){
int len = cnt[root].size();
if(!len){
return 0;
}
int Max = -1, loc1 = -1, loc2 = -1;
for(int i = 0; i < len; i++){
if(dfs1(cnt[root][i].first) + cnt[root][i].second > Max){
Max = dfs1(cnt[root][i].first) + cnt[root][i].second;
loc1 = i;
}
}
dp1[root] = Max;
Max = -1;
A[root] = loc1;
for(int i = 0; i < len; i++){
if(dp1[cnt[root][i].first] + cnt[root][i].second > Max && i != loc1){
Max = dp1[cnt[root][i].first] +cnt[root][i].second;
loc2 = i;
}
if(loc2 != -1)B[root] = Max;
}
return dp1[root];
}
void dfs2(int root){
int len = cnt[root].size();
for(int i = 0; i < len; i++){
if(i == A[root]){
dp2[cnt[root][i].first] = max(dp2[root], B[root]) + cnt[root][i].second;
}
else{
dp2[cnt[root][i].first] = max(dp2[root], dp1[root]) + cnt[root][i].second;
}
dfs2(cnt[root][i].first);
}
}
int main(){
while(scanf("%d",&n) != EOF){
memset(dp1, 0, sizeof(dp1));
memset(B, 0, sizeof(B));
memset(A, 0, sizeof(A));
memset(dp2, 0, sizeof(dp2));
for(int i = 1; i <= INF; i++){
cnt[i].clear();
}
for(int i = 2; i <= n; i++){
int x1, x2;
scanf("%d%d", &x1, &x2);
cnt[x1].push_back(make_pair(i, x2));
}
dfs1(1);
dfs2(1);
for(int i = 1; i <= n; i++){
cout<<max(dp1[i], dp2[i])<<endl;
}
}
return 0;
}
吃糖直播间
首先考虑最简单的情况。如果只有一个箱子,则选择该箱子,可以选择到最高总糖果数。如果只有两个箱子,则由于两个箱子相邻,不能同时选择,只能选择其中的一个箱子,因此选择其中糖果数较高的箱子进行选择,可以选择到最高总糖果数。
如果箱子数量大于两个,应该如何计算能够选择到的最高总糖果数呢?对于第 k(k>2) 个箱子,有两个选项:
1.选择第 k 个箱子,那么就不能选择第 k-1 个箱子,选择总糖果数为前 k-2 个箱子的最高总糖果数与第 k 个箱子的糖果数之和。
2.不选择第 k 个箱子,选择总糖果数为前 k-1 个箱子的最高总糖果数。
在两个选项中选择选择总糖果数较大的选项,该选项对应的选择总糖果数即为前 k 个箱子能选择到的最高总糖果数。
用 dp[i] 表示前 i 个箱子能选择到的最高总糖果数,那么就有如下的状态转移方程:
dp[i]=max(dp[i−2]+nums[i],dp[i−1])
边界条件为:
{
dp[0]=nums[0] 只有一个箱子,则吃该箱子中的糖果
dp[1]=max(nums[0],nums[1]) 有两个箱子,选择其中糖果数多的箱子。
}
最终的答案即为dp[n−1],其中 n 是数组的长度。
#include
#include
using namespace std;
const int maxn = 110;
int arr[maxn], dp[maxn][5];
int main() {
int t, cas = 0;
cin >> t;
while (t--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> arr[i];
}
dp[1][0] = 0;
dp[1][1] = arr[1];
for (int i = 2; i <= n; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1]);
dp[i][1] = dp[i - 1][0] + arr[i];
}
printf("Case #%d:\n", ++cas);
cout << max(dp[n][0], dp[n][1]) << endl;
}
}
我师傅实在太稳健了
我觉得这个看了代码秒懂,这个也是签到题吧…
#include
using namespace std;
int main() {
int t;
cin >> t;
while (t--) {
int a, b, c, d, e;
cin >> a >> b >> c >> d >> e;
if (a - b - c - d - e > 1 ||
b - a - c - d - e > 1 ||
c - d - e - a - b > 1 ||
d - e - a - b - c > 1 ||
e - a - b - c - d > 1) {
cout << "no\n";
} else {
cout << "yes\n";
}
}
return 0;
}
你看,是不是秒懂…
大奉卖笔人
大致思路如下:
1、先对输入的数组进行快速排序,从小到大排列
2、先进行第一步优化,将可以去掉的球先去掉,使后面的连续几类球都保持一样的数量,
在此过程中去掉的球的总价格计算进total里面,
这一步优化目的在于找到一个阈值threshold,使得去掉的球的数量threshold <= orders
一旦threshold > orders,结束此步。
这一步结束后,color_count表示优化了多少个类的球的数量,base表示下一步优化时球的基准,
optimize表示没有超过orders的最大threshold,即这一步去掉的球的总数。
例如:[2 4 6 8 10] orders = 20
-> [2 4 6 8 8] threshold = 10-(8×1) = 2 < orders
-> [2 4 6 6 6] threshold = (10+8)-(6×2) = 6 < orders
-> [2 4 4 4 4] threshold = (10+8+6)-(4×3) = 12 < orders
-> [2 2 2 2 2] threshold = (10+8+6+4)-(2×4) = 20 = orders
所以从[2 4 6 8 10]优化后是[2 2 2 2 2]
color_count = 4, base = 2,threshold = 20,optimize = 20
[2 4 6 8 10] orders = 23
-> [2 2 2 2 2] threshold = (10+8+6+4)-(2×4) = 20 < orders
-> [0 0 0 0 0] threshold = 10+8+6+4+2 = 30 > orders
所以从[2 4 6 8 10]优化后是[2 2 2 2 2]
color_count = 4, base = 2,threshold = 30,optimize = 20
3、这一步结束之后,我们已经去掉的球的数量是optimize,剩下的球的数量是orders-optimize
如果orders = optimize,直接返回total,就是最终的价格
否则再进行进一步优化
在第2步中的优化是从球最多的开始,每次优化1个,2个…n个球的数量
而在这一步中,现在要一次优化color_count个球的数量
在优化的过程,去掉的球的总价格就是color_count个等差数列的和
先计算(orders-optimize)/(color_count+1),得到等差数列的n
然后计算(order-optimize)%(color_count+1),得到计算完等差数列后剩下几个单独的球需要取出
a1 = base - n + 1
an = base
然后根据a1,an,n计算出等差数列的和,color_count个等差数列的和就是这一次优化的球的总价格
最后还剩下几个球要单独取出,它们的价格是base-n,数量 < color_count + 1
将批量的和单独的球的总价格都加进total里
最后得到total就是结果
注意在计算过程中注意数字溢出的问题
#include
using namespace std;
void quickSort(int arr[], int low, int high, int size);
int maxProfit(int *inventory, int inventorySize, int orders);
int main() {
int num;
cin >> num;
int size;
int inventory[100000];
int orders;
while (num--) {
cin >> size;
for (int i = 0; i < size; i++) {
cin >> inventory[i];
}
cin >> orders;
cout << maxProfit(inventory, size, orders) << "\n";
}
}
void quickSort(int arr[], int low, int high, int size) {
if (size == 1)
return;
if (size == 2) {
if (arr[0] >= arr[1]) {
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
return;
} else
return;
}
if (low >= high)
return;
int first = low;
int last = high;
int key = arr[first];
while (first < last) {
while (first < last && arr[last] >= key) {
last--;
}
arr[first] = arr[last];
while (first < last && arr[first] < key) {
first++;
}
arr[last] = arr[first];
}
arr[first] = key;
quickSort(arr, low, first - 1, size);
quickSort(arr, first + 1, high, size);
}
int maxProfit(int *inventory, int inventorySize, int orders) {
quickSort(inventory, 0, inventorySize - 1, inventorySize);
long int total = 0;
if (inventorySize == 1) {
long int a1 = inventory[0] - orders + 1;
total = ((long int) orders * (a1 + (long int) inventory[0]) / 2) % 1000000007;
return total;
}
int color_count = 0;
long int balls_count = 0;
long int threshold = 0;
int optimize = 0;
for (int m = inventorySize - 1; m >= 0; m--) {
balls_count += (long int) inventory[m];
if (m > 0)
threshold = balls_count - (long int) (inventorySize - m) * inventory[m - 1];
else
threshold = balls_count;
if (threshold <= (long int) orders) {
optimize = threshold;
color_count += 1;
} else
break;
}
int base = 0;
if (color_count == inventorySize)
base = 0;
else
base = inventory[inventorySize - color_count - 1];
for (int n = 0; n < color_count; n++) {
total = (total +
(long int) (inventory[inventorySize - n - 1] - base) * (base + 1 + inventory[inventorySize - n - 1]) /
2) % 1000000007;
}
if (optimize == orders)
return total;
int left = orders - optimize;
int times = left / (color_count + 1);
int single = left % (color_count + 1);
long int times_total = 0;
int a1 = base - times + 1;
times_total =
((long int) times * ((long int) a1 + (long int) base) / 2 * (long int) (color_count + 1)) % 1000000007;;
total = (total + times_total) % 1000000007;
total = (total + (long int) (a1 - 1) * (long int) single) % 1000000007;
return total;
}
最后预祝各位大佬在现场赛都能取得好成绩