nowcoder
第k小
我沒有搞清楚題目的意思。以為要全部保留所有的數,其實不然
題目只需要保留到第k位即可,我們可以開一個優先佇列去存值
查詢小於k輸出-1,大於k,pop掉
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n,m,k;
cin>>n>>m>>k;
priority_queue<int,vector<int>,less<int>>q;
for (int i = 0; i < n; i++) {
int a;
cin>>a;
q.push(a);
}
while(q.size()>k)q.pop();
while(m--){
int b;
cin>>b;
if(b!=1){
if(k>q.size())cout<<-1<<'\n';
else
cout<<q.top()<<'\n';
}else{
int c;
cin>>c;
q.push(c);
if(q.size()>k)q.pop();
}
}
return 0;
}
Running Median
使用對頂堆可解,左邊的堆維護中位數前半部分,右邊的堆維護中位數後半部分。在新的數進來的時候判斷應該插左邊還是右邊,然後對左右如果差值超過1進行調整。
這樣要麼左堆的頂端是中位數,要麼當左右相同大小的時候就是兩個堆頂的數取平均數。
沒有怎麼練過這種題,對頂堆去維護某個值
#include <bits/stdc++.h>
using namespace std;
int p;
int main() {
cin>>p;
while (p--) {
priority_queue<int, vector<int>, less<int> > zuo;
priority_queue<int, vector<int>, greater<int> > you;
int num, m, val;
cin>>num>>m;
vector<int> v;
for (int i=1;i<=m;i++) {
cin>>val;
if (zuo.size()==you.size()) {
if (zuo.size()==0) {
zuo.push(val);
} else if (you.top()<val)
{
you.push(val);
zuo.push(you.top());
you.pop();
} else {
zuo.push(val);
}
}else {
if (zuo.top()>=val) {
zuo.push(val);
you.push(zuo.top());
zuo.pop();
} else {
you.push(val);
}
}
if (i&1==1) {
if (!zuo.empty()) {
if (zuo.size()==you.size()) v.push_back((zuo.top()+you.top())/2);
else v.push_back(zuo.top());
}
}
}
cout<<num<<" "<<v.size()<<endl;
int i;
for (i=0;i<v.size();i++) {
cout<<v[i]<<" ";
if ((i+1)%10==0) {
cout<<endl;
}
}
if (i%10!=0)
cout<<endl;
}
return 0;
}
tokitsukaze and Soldier
我想到怎麼去分堆去堆放,但是沒有想到怎麼去維護每個s[i]的最大時攻擊力最大,從大佬的
思路得知,我們從後往前插入優先佇列,一直往前加,如果當前的人數大於對堆內最小的k,那麼把堆內最小的攻擊力推出去
這樣就能維護每個s[i]的最大攻擊力。
思路:我們考慮按s[i]存入vectoc,從大到小列舉s[i]的值。那麼就是在所有>=s[i]計程車兵中選v最大的s[i]個。我們可以優先佇列維護。因為s[i]是減小的。所以刪除計程車兵一定在後面用不到。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
priority_queue<LL, vector<LL>, greater<LL> > q;
vector<LL> ve[100005];
int main(){
int n; scanf("%d", &n);
for(int i=1; i<=n; i++){
LL v, s;
scanf("%lld%lld", &v, &s);
ve[s].push_back(v);
}
LL ans=0, sum=0;
for(int i=n; i>=1; i--){
for(auto u: ve[i]){
sum+=u;
q.push(u);
}
while(q.size()>i){
sum-=q.top();
q.pop();
}
ans=max(ans, sum);
}
printf("%lld\n", ans);
return 0;
}
[JSOI2007]建築搶修
貪心+優先佇列,
貪心策略:
有三種
- 按需要時間短的來做
- 按最晚開始時間做(結束減去修時間)
3.按截止的時間早晚來做
第一種雖然時間短,但是截止時間晚,做了這件事,耽誤你之前做的時間截止少的事
第二種開始時間早,但是修理時間長,那肯定也不行,修理時間完全可以做更多的事
第三種按照截止時間做,如果這件事做時間很長,但是後面已經可以做兩件或者更多事
這個時候是有問題的。導致我們本來可以做後面兩件事卻被這件事耽誤了
大佬的題解
#include <iostream>
#include <algorithm>
#include <utility>
#include <queue>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
//程式碼預處理區
const int MAX = 1e5 + 5e4 + 7;
struct Node {
int repair, time;
bool operator < (const Node& v) const {
if (time == v.time) return repair < v.repair;
return time < v.time;
}
} info[MAX];
priority_queue<int, vector<int>, less<int> > pq;
//全域性變數區
int main() {
IOS;
int n; cin >> n;
for (int i = 1; i <= n; i++) cin >> info[i].repair >> info[i].time;
sort(info + 1, info + 1 + n);
int time = 0;
for (int i = 1; i <= n; i++) {
if (time > info[i].time) continue;//已經報廢了
if (time + info[i].repair <= info[i].time) {
time += info[i].repair;
pq.push(info[i].repair);
}
//如果可以修就直接修
else {
if (pq.top() > info[i].repair) {
time -= pq.top() - info[i].repair;
pq.pop(); pq.push(info[i].repair);
}
}
//修不了就判斷他是不是比已經修了的中最划不來的划得來,划得來就換這個
}
cout << pq.size() << endl;
return 0;
}
選擇困難症
這道題就是一道簡單的DFS,但是我不知道怎麼剪枝老是出問題
正確的方法是我們每sum>k時,把後面的情況全部算進去,這樣就剪枝了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105;
int k,m;
int a[10];
int v[10][N];
ll ans=0;
void dfs(int u,int val)
{
if(u>k+1) return;
if(val>m)
{
ll temp=1;
for(int i=u;i<=k;i++)
{
temp*=(a[i]+1);
}
ans+=temp;
return;
}
for(int i=1;i<=a[u];i++)
{
dfs(u+1,val+v[u][i]);
}
dfs(u+1,val);
}
int main()
{
while(cin>>k>>m)
{
ans=0;
for(int i=1;i<=k;i++)
{
cin>>a[i];
for(int j=1;j<=a[i];j++)
{
scanf("%d",&v[i][j]);
}
sort(v[i]+1,v[i]+1+a[i]);
}
dfs(1,0);//層數 價值
cout<<ans<<endl;
}
return 0;
}
codeforces
C. Mad MAD Sum
這道題不是一點不會做,而是題目要搞清楚,要去手打幾遍,要去打表找規律
當我們跑一次發現,數列已經是不下降的了,第二次跑完已經把所有數量為1的數全部刪除了
最後剩下的都是大於等於2的數,我們把每個數加的次數累加上即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
int32_t main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int sum=0;
int a;
cin>>a;
vector<int>v(a+1);
for (int i = 1; i <=a ; ++i) {
cin >> v[i];
}
for(int i=1;i<=2;i++){
int maxx=0;
unordered_map<int,int>mp;
for(int j=1;j<=a;j++){
sum+=v[j];
mp[v[j]]++;
if(mp[v[j]]>1)maxx=max(maxx,v[j]);
v[j]=maxx;
}
}
for(int i = 1 ; i <= a ; i ++) {
sum += v[i] * (a-i+1);
}
cout << sum << "\n";
}
return 0;
}
C. Basil's Garden
dp思維,從後面狀態轉移到前面,如果前面的數不大於後面的和,前面的數由後面轉移加1,否則變成前面的數
#include <bits/stdc++.h>
using namespace std;
#define int long long
int f[1008611];
int32_t main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
int n;
cin>>n;
int maxx=0;
vector<int>v(n+1);
for (int i = 1; i <=n ; i++) {
cin>>v[i];
}
f[n]=v[n];
for(int j=n-1;j>=1;j--){
if(v[j]>f[j+1]){
f[j]=v[j];
}else{
f[j]=f[j+1]+1;
}
}
cout<<f[1]<<'\n';
}
return 0;
}