A. Restoring Three Numbers
题意
给出四个数:a+b
、a+c
、b+c
、a+b+c
,要求输出a、b、c
关键词
数学
思路
四个数中,最大的数一定是a+b+c
用这个数减去其他三个数的结果,就是a、b、c。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 300005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
int num[4];
for (int i = 0; i < 4; ++i)
{
cin>>num[i];
}
sort(num,num+4);
int a, b, c;
b = num[3]-num[1];
a = num[3] -num[2];
c = num[3] -num[0];
cout<<a<<" "<<b<<" "<<c<<endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
B. Make Them Equal
题意
给定一个长度为n的数组,可以对数组中每一个数都加上或减去同一个数D
,如果存在使得数组所有数都相等的D
则输出D
,否则输出-1
。
关键词
数学
思路
用set来保存数组中有多少个不同的数,并对set的大小size
分成3种情况进行讨论:
- size=3:如果三个数构成等差数列,则结果为等差数列的差,否则无解。
- size=2:这种情况一定有解,如果两数之差为奇数,则结果就为他们的差,如果为偶数,则可以再除以二。
-
size=1:显然,这种情况数组中的数已经是相等的,结果为
0
。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 1000
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int n;
int arr[MAXN];
set<int> se;
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
cin>>n;
for (int i = 0; i < n; ++i)
{
cin>>arr[i];
se.insert(arr[i]);
}
int sum = 0;
vector<int> vt;
for (int i : se)
{
vt.pb(i);
}
if(se.size() == 3)
{
int a = vt[0];
int b = vt[1];
int c = vt[2];
if(c-b != b-a)
{
cout<<-1<<endl;
} else
cout<<b-a<<endl;
}else if(se.size() == 2)
{
int ans = vt[1] - vt[0];
if(ans % 2 == 0 )
ans /= 2;
cout<<ans<<endl;
}else if(se.size() == 1)
{
cout<<0<<endl;
} else
{
cout<<-1<<endl;
}
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
C. Gourmet Cat
题意
给出a、b、c三种食物的数量,并规定一周中每天吃的食物:
周一 | 周二 | 周三 | 周四 | 周五 | 周六 | 周日 |
---|---|---|---|---|---|---|
a | b | c | a | c | b | a |
可以从任意一天开始,求最多能连续吃多少天。
关键词
贪心、暴力
思路
显然一周中,需要消耗a
3份,b
2份,c
2份。
对于给定的食物数量,按照这个比例可以求出能完整地吃多少周。
再求出无法连续吃一周时,剩余每种食物的数量。
从周一到周日开始暴力枚举,求出这些剩余的食物最长能连吃多少天。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 700000005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
int a, b, c;
cin >> a >> b >> c;
ll ans = 7 * min(a / 3, min(b / 2, c / 2));
if (ans)
{
a -= ans / 7* 3;
b -= ans / 7* 2;
c -= ans / 7* 2;
}
int ta = a, tb= b, tc = c;
int d[] = {1, 2, 3, 1, 3, 2, 1};
int m = 0;
for (int i = 0; i < 7; ++i)
{
int t = 0;
int ta = a, tb= b, tc = c;
for (int j = i, k = 0; k < 7; ++k, j = (i+k)%7)
{
if (d[j] == 1)
{
if (ta)
{
ta--;
t++;
} else
{
break;
}
} else if (d[j] == 2)
{
if (tb)
{
tb--;
t++;
} else
break;
} else
{
if (tc)
{
tc--;
t++;
} else
break;
}
}
m = max(m, t);
}
ans += m;
cout << ans << endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
D. Walking Robot
题意
给定一个拥有一个普通电池(用完就不可以再使用)和一个太阳能电池(在一定条件下可充电)的机器人。
两块电池开始时电量都是满的,且容量分别为b(普通电池)、a(太阳能电池)。且每走一段路程都需要消耗一个单位的电量。
给定n段路程,当该段路程可以给太阳能电池充电时,用1
表示,位于该段路程中,可以通过使用普通电池行走,使太阳能电池的电量+1,但不能超过其容量。
求机器能最长能走多远。
关键词
贪心、构造
思路
在不能给太阳能电池充电时
或可以充电但太阳能电池的电量已满时
,优先使用太阳能电池。其他情况使用普通电池。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 200005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int arr[MAXN];
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
int n, b, a;
cin >> n >> b >> a;
int c = a;
int ans = 0;
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
if (arr[i])
{
if (c && c == a)
{
c--;
} else if (b)
{
b--;
c++;
} else if (c)
{
c--;
} else
{
ans = i;
break;
}
} else
{
if (c)
{
c--;
} else if (b)
{
b--;
} else
{
ans = i;
break;
}
}
}
if( ans == 0)
ans = n;
else
ans --;
cout << ans << endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
E. Two Teams
题意
给一个长度为n
的学生初始队伍,按照每次选择初始队伍中数值最大的学生和他左右最多各k
个学生,分配到另外两个队伍。
要求打印分配结果。
关键词
构造、排序、数据结构、双向链表
思路
记录每一个数值的学生所在的位置,并使用数组(方便根据数值直接拿到学生的位置)维护一个类似双向链表的数据结构。
对于每一个初始队伍中的学生,维护他左边和右边相邻第一个学生的坐标。
从初始队伍移除学生时,使用这个学生维护其左右相邻学生的相邻学生坐标。类似于双向链表中删除某个节点时,更新前后节点指针的操作。
对学生排序。
每次移除数值最高的,并向左右继续移除最多k
个学生。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 200005
#define MAXM 200
#define MAXK 10000010
#define MOD 6000
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
#define MSET(arr,v) memset((arr),(v),sizeof(arr))
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int sk[MAXN], le[MAXN], ri[MAXN];
int arr[MAXN];
int ans[MAXN];
void remove (int x)
{
le[ri[x]] = le[x];
ri[le[x]] = ri[x];
}
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
int n, k;
MSET(ans, 0);
cin >> n >> k;
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
sk[arr[i]] = i;
le[i] = i - 1;
ri[i] = i + 1;
}
int team = 1;
for (int i = n; i >= 0; --i)
{
int index = sk[i];
if(ans[index])
continue;
remove(index);
ans[index] = team;
int l = le[index], r = ri[index];
for (int cnt = 0; cnt < k && l>0; ++cnt, l = le[l])
{
if(!ans[l])
{
remove(l);
ans[l] = team;
}
}
for (int cnt = 0; cnt < k && r<=n; ++cnt, r = ri[r])
{
if(!ans[r])
{
remove(r);
ans[r] = team;
}
}
team = 3 - team;
}
for (int i = 0; i < n; ++i)
{
cout<<ans[1+i];
}
cout<<endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
F. Shovels Shop
题意
给定n
件物品的价格,每种物品只有一件,以及m
种优惠。
每种优惠的都可以表示为,一次恰好购买x
件物品时,最便宜的y
件都免费。不限制优惠的使用次数。
问不限制购买次数且每次都可以购买任意物品的情况下,购买k
个物品最少需要花多少钱。
关键词
dp、贪心
思路
类似于背包问题的解法:
使用dp[i]
表示恰好购买i件物品花费的最少金额,显然一定是最便宜的i
件。
- 先对价格排序,并求累加和
sum
(sum(i)
表示物品位置在[1, i]的价格累加和,sum(a, b)
表示物品位置在[a, b]的价格的累加和) - 初始值:
dp[i] = sum(i)
,dp[0] = 0
。 - 对于每个数量
i
、每种优惠的x
、y
,状态转移方程为:
dp[i] = min(dp[i], dp[i-x] + sum(i-x+y+1, i))
。
状态转移方程的含义:
对于购买当前数量i
的物品所花费最小金额的状态,都可以通过在dp[i-x]
状态的基础上使用某种优惠来达到,使用该优惠的花费为sum(i-x+y+1, i)
。
对于状态i
,取所有使用的优惠中,花钱最少的那种即可。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 200015
#define MAXM 200
#define MAXK 10000010
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);
#define MSET(arr, v) memset((arr),(v),sizeof(arr))
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int n, m, k;
int arr[MAXN];
Pair pur[MAXN];
ll dp[MAXN] = {0};
ll sum[MAXN] = {0};
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
cin >> n >> m >> k;
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
}
for (int i = 0; i < m; ++i)
{
cin >> pur[i].first >> pur[i].second;
}
sort(arr+1, arr + n+1);
for (int i = 1; i <= n; ++i)
{
sum[i] = sum[i - 1] + arr[i];
dp[i] = sum[i];
}
dp[0] = 0;
for (int i = 0; i <= k; ++i)
{
for (int j = 0; j < m; ++j)
{
int x = pur[j].first;
int y = pur[j].second;
if(i+x > k)
continue;
dp[i + x] = min(dp[i + x], dp[i] + sum[i + x] - sum[i + y]);
}
}
cout << dp[k] << endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}
F. Minimum Possible LCM
题意
给定n个数,求出其中任意两个数的最小公倍数的最小值为多少。
关键词
暴力、贪心、数论
思路
显然,至少出现过2次的最小那个的数,一定是最优的结果。
对于其他情况:
枚举一个公因子i
,如果给定的数中存在i
的两个不同的倍数,则通过i
计算这两个数的公倍数去维护最终结果尽可能小。
即:
对于i
,如果存在两个数A
、B
满足A=a*i
、B=b*i
(a
和b
为不同整数,这意味着i
为A
、B
的公因子),则ans = min(ans, A*B/i) = min(ans, a*b*i)
。
需要注意的是,如果整数a
、b
不互质,则意味着i
不是最大公因子,此时A*B/i
并不会是A
、B
的最小公倍数,而是一个大于最小公倍数的值。
但是这并不影响最终结果,因为,在后面对i
的枚举中,早晚会枚举到A
、B
的最小公倍数,最终结果会维护为最小的值。
例如,
i=2
,A=4
、B=8
,此时的a=2
,b=4
。算出来的公倍数为A*B/i = 16
,并不是A
、B
的最小公倍数。
但是在接下来枚举到i=4
的时候,对于A=4
、B=8
就有a=1
,b=2
。此时算出来的就是最小公倍数A*B/i = 8
。
因为最终结果会取每次枚举结果的最小值,这里为8
,所以前面i=2
时虽然不是最优解,但不会影响后面真正的最优解的求解。
代码
#include <bits/stdc++.h>
#define Debug 0
#define MAXN 10000005
#define MAXM 200
#define MAXK 10000010
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define PI 3.1415926535
#define pb push_back
#define SYNC ios::sync_with_stdio(false);s
#define MSET(arr, v) memset((arr),(v),sizeof(arr))
//#define FILE_IN "I:\\acm-workspace\\CLion-Project\\in.txt"
typedef long long ll;
typedef std::pair<int, int> Pair;
using namespace std;
int n;
ll arr[MAXN];
int index[MAXN] = {0};
int main ()
{
#ifdef FILE_IN
freopen(FILE_IN, "r", stdin);
#endif
// SYNC
cin >> n;
ll mi = LONG_LONG_MAX;
int a, b;
for (int i = 1; i <= n; ++i)
{
cin >> arr[i];
// If the smallest number appears twice, this number will be the optimal solution.
if (index[arr[i]] && arr[i] < mi)
{
mi = arr[i];
a = index[arr[i]];
b = i;
}
index[arr[i]] = i;
}
for (int i = 1; i < MAXN; ++i)
{
int aa = 0, bb = 0;
ll x, y;
for (int j = i; j < MAXN; j += i)
{
if (index[j] && aa > 0)
{
y = j;
bb = index[y];
break;
} else if (index[j])
{
x = j;
aa = index[x];
}
}
if(!aa || !bb)
continue;
ll lcm = x * y / i;
if (lcm < mi)
{
mi = lcm;
a = aa;
b = bb;
}
}
if (a > b)
swap(a, b);
cout << a << " " << b << endl;
#ifdef FILE_IN
fclose(stdin);
#endif
return 0;
}