A - A CodeForces - 1257E
这个是原题,主要部分就是求最长上升序列的方法,用二分法能够AC!
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
#include <iomanip>
#include <map>
#include <queue>
#include <vector>
#include <set>
typedef long long ll;
using namespace std;
const int N = 2e5 + 5;
int s[N];
int main()
{
int k1, k2, k3; cin >> k1 >> k2 >> k3;
int n = k1 + k2 + k3;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
sort(s + 1, s + 1 + k1); sort(s + 1 + k1, s + 1 + k2 + k1);
sort(s + k2 + 1 + k1, s + 1 + n);
int maxx = 1;
int f[N];
int top =1;
f[1]=-1;
for (int i = 1; i <=n; i++)
{
int t = s[i];
if (t > f[top])
f[++top] = t;
else
{
int l =1, r = top;
int mid;
while (l <= r)
{
mid = (l + r) / 2;
if (t > f[mid])
{
l = mid + 1;
}
else
r = mid - 1;
}
f[l] = t;
}
}
cout << n-top+1<< endl;
}
CodeForces - 1256E
这个题是第二次做了,第一次自己没认真,这次一定要掌握!
题意:至少三个人一组,然后你要想办法让所有的组内的最大值最小值相差 的 所有的和最小,然后要输出他们的组的编号!
思路:最小3个人一组,其实最多也就5个人一组,再多的话就能分成两组了,这样 差就更小了,所以至多五个人,这个题用dp来做,设一个数组 dp[i] 表示前i个人所得到的最小 ‘差和’,然后在每一个i时,去枚举以i为开头的组的规模(3–5),用dp来记录min,同时把i存到L数组中,这个是为了后面表组号!
后面组号就是 开头那个人是 L[h],那么结尾那个人是h。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
#include <iomanip>
#include <map>
#include <queue>
#include <vector>
#include <set>
typedef long long ll;
using namespace std;
const int N = 2e5 + 5;
const int maxm = 2e5 + 5;
const int inf = 1e9;
struct Node
{
int v, id;
}a[maxm];
int d[maxm];//d[i]表示分配前i个人需要的最小花费
int L[maxm];
int ans[maxm];
int n;
int cmp(Node a, Node b)
{
return a.v < b.v;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin>>a[i].v;
a[i].id = i;
}
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; i++) //初始化inf
d[i] = inf;
for (int i = 1; i <= n; i++)
{
for (int j = 3;j <= 5; j++)//枚举队伍的长度(此时i为开头)
{
int t = d[i-1] + a[i+j-1].v - a[i].v;//花费为前i-1个人的花费d[i-1]加上a[i+j-1].v-a[i].v
if (t< d[i + j - 1]) {
d[i + j - 1] = t;
L[i+j-1] = i;//记录区间左端点(右端点为i+j-1)
}
}
}
int now = n;
int cnt = 0;
while (now)
{//标记队伍编号
cnt++;
for (int i = L[now]; i <= now; i++) {
ans[a[i].id] = cnt;
}
now = L[now] - 1;
}
cout << d[n] << " " << cnt << endl;
for (int i = 1; i <= n; i++)
{
cout << ans[i] << " ";
}
cout << endl;
return 0;
}
CodeForces - 1260E
题意:输入数字N,1-N分别代表着n个拳击手的战斗力,然后每个拳击手都可以被贿赂,每个人都有贿赂的金额,为了得到第一,你要付出多少金额,每次都是两两对决,获胜者进入下一轮。
思路:每次都是两两对决,假设你的位置是sign,i<sign的不贿赂了,你一定赢,所以你要管的是i>sign的部分,每次比赛完还剩多少人?,其实就是每次都÷2,,用一个优先队列储存[k,2*k)贿赂钱,每次都贿赂top()就可以。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
#include <iomanip>
#include <map>
#include <queue>
#include <vector>
#include <set>
typedef long long ll;
using namespace std;
const int N =1000000;
ll a[N],b[N];
int main()
{
priority_queue<ll, vector<ll>, greater<ll> > q;
ll n; cin >> n; ll s = 0;
ll sign; int c = 1;
for (ll i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] == -1)
sign = i;
}
ll k = n;
while (k > sign)
{
for (ll i = k; i <= n && i < 2 * k; i++)
q.push(a[i]);
s += q.top();
q.pop();
k = k / 2;
}
cout << s << endl;
}
D - D CodeForces - 1272E
题意:一个长度为 n 的序列,每个位置可以跳到 i−ai 或者 i+ai求出每个位置最少需要跳几次,可以使起始位置和结束位置的奇偶性不同。
思路:建立一个vector容器 ,将某个数能到达的位置 为下标 将i存入,然后对每个i进行一次遍历,根据i点的奇偶性,若为奇数值,那么它上一个点到奇数值点的最短距离只有1一种可能。i的x值更新为1,而y记录的是i点到下一个y点的距离,然后依次类推。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
#include <iomanip>
#include <map>
#include <queue>
#include <vector>
#include <set>
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int N = 2e5 + 5;
const int maxm = 2e5 + 5;
const int maxn = 200005;
using namespace std;
struct node
{
int val, x, y;
}a[maxn];
vector<int> v[maxn];
int main()
{
int n;
cin >> n;
for (int i = 1; i <= n; i++)
{
a[i].x = a[i].y = inf;
cin >> a[i].val;
if (i + a[i].val <= n)
v[i + a[i].val].push_back(i);
if (i - a[i].val>=1)
v[i - a[i].val].push_back(i);
}
queue<int>q;
for (int i = 1; i <= n; i++)
{
q.push(i);
}
while (!q.empty())
{
int head = q.front();
q.pop();
for (int i : v[head])//v[head]中放的是下一次移动能到达head位置的点的编号,即上一个点的编号
{
if (a[head].val%2==1)//如果head点值为奇数
{
if (a[i].x>1|| a[i].y > a[head].y + 1)//head点为奇数值,那么它上一个点到奇数值点的最短距离只有1一种可能。i的x值更新为1,而y记录的是head点到下一个y点的距离
{
a[i].x = 1;
if (a[i].y > a[head].y + 1)//位置i的点用对应的y继承位置head点到值为偶数的点的最短距离
a[i].y = a[head].y + 1;
q.push(i);
}
}
else
{
if (a[i].y > 1 || a[i].x > a[head].x + 1)
{
a[i].y = 1;
if (a[i].x > a[head].x + 1)
a[i].x = a[head].x + 1;
q.push(i);
}
}
}
}
for (int i = 1; i <= n; i++)
{
if (a[i].val%2==1)
{
cout << (a[i].y == inf ? -1 : a[i].y) << " ";
}
else
cout << (a[i].x == inf ? -1 : a[i].x) << " ";
}
}
E挺简单的 不说了
FCodeForces - 1251D
第二次!
题意: 给n个人发工资,总钱数为s。每个人有一个工资范围。要求一个发工资方案,使得工资中位数最大,求这个中位数。
思路:假设当前中位数为x,我们只需要保证有n/2+1个位置包含x就可以了。按照左端点由小到大排序(pair 先按first 其次按second),然后倒序去找,这样我们可以优先考虑>=x的数,同时需要判断最小价值是否大于s以及是否有n/2+1个数>=x.
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <algorithm>
#include <iomanip>
#include <map>
#include <queue>
#include <vector>
#include <set>
#define inf 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int MAXN = 200005;
const int INF = 1e9;
vector<pair<ll, ll>> v;
ll n, s, x, y;
bool gg(ll x)
{
ll t = s, cnt = n / 2 + 1;
for (int i = n - 1; i >= 0; i--)
{
if (v[i].second >= x && cnt > 0)
{
cnt--;
t -= max(x, v[i].first);
}
else
{
t -= v[i].first;
}
}
return (!cnt) && t >= 0;
}
int main()
{
int T;
cin >> T;
while (T--)
{
v.clear();
cin >> n >> s;
for (int i = 0; i < n; i++)
{
cin >> x >> y;
v.push_back(make_pair(x, y));
}
sort(v.begin(), v.end());
ll l = 0, r = INF;
while (l <= r)
{
int mid = (l + r) >> 1;// ==/2!;
if (gg(mid)) l = mid + 1;
else r = mid - 1;
}
cout << l - 1 << endl;
}
}
反思:这个还是出现了原题自己没AC ,那些都是之前感觉太难了,就没认真看的题目,自己总是知难而退,我以后会认真处理题目,写过的题解也不能光当做作业,我也要学习群里同学 的做法,常常回看,重做题目,来提高自己的思想水平!