補題1:奶茶袋收集
題意:
做法:貪心。之前還做過類似的題,賽時一直想不出來。選擇k個連續的的區間,就是需要新增k-1個擋板。問題是擋板設定在哪裡?可以發現一個連續線段的max-min等於線段中各個差值之和。如果k=1,那麼ans=∑(ai+1-ai);如果k=2,那麼需要新增一個擋板。貪心地放,擋板應該放在最大的ai+1-ai中。然後答案為剩下的ai+1-ai。
void solve(){ //補L1-7--奶茶袋收集-貪心。之前寫過一模一樣的題。。現在還想歪了,往字首和想。。
int n,k,x,ans=0,last=-1;
cin>>n>>k;
vector<int> dif;
for(int i=1;i<=n;i++){
cin>>x;
if(i>=2) dif.emplace_back(x-last);
last=x;
}
sort(dif.begin(),dif.end(),greater<int>());
for(auto d:dif){
if(k>1) k--;
else ans+=d;
}
cout<<ans;
}
補題2:swj學長的融合
題意:
做法:題目本身完全沒有難度,主要是讀題目沒讀懂升級機制是什麼,不知道怎麼樣“以此類推”。賽後讀懂題目之後輕鬆補了。
vector<pair<int,int>> vct[100005];
int x,m,a,b,c,d,ans=0;
void dfs(int step){
for(auto to:vct[step]){
ans+=to.second;
dfs(to.first);
}
}
void solve(){ //補L2-2--swj學長的精靈融合--建圖dfs..賽時題目都沒讀懂,不知道等級升級系統(類似金剷剷)是怎麼樣都,不知道怎麼以此類推..
cin>>x>>m;
for(int i=1;i<=m;i++){
cin>>a>>b>>c>>d;
int exp=1;
if(d==1) exp=0;
if(c==1) exp+=( (d-2) * (1+d-2) )/2;
if(c==2) exp+=( (d-2) * (2+2*(d-2) ) )/2;
if(c==3) exp+=( (d-2) * (5+5*(d-2)) )/2;
//cout<<exp<<endl;
vct[a].emplace_back(b,exp);
}
dfs(x);
cout<<ans;
}
補題3:紅石難題
題意:
做法:思維(關鍵)+貪心--把二維座標軸拉成一條數軸(常有的做法)轉換為一維的,之後進行簡單貪心即可。
注意的點!:輸出的時候如果直接用了ceil,要控制輸出小數點,ceil返回的不是整型,輸出會帶小數點。還有就是特判m=0的情況;
還有最最重要一點!length初始值是1,因為計算的是長度,實際上要的是數軸上端點的個數。而起始點上沒有算上的,要自己加上!!!!比如長度為5的數軸,是有6個點的。長度是端點間的間隙,有5個間隙的話,那麼需要6個點。
void solve(){ //補L1-5紅石難題--思維(關鍵)+貪心--把二維座標軸拉成一條數軸(常有的做法)轉換為一維的,之後進行簡單貪心即可-好題!
//有一個很關鍵的點!!--紅石訊號的傳導只和他放置的紅石順序有關!--這是可以拉成一條數軸的關鍵,傳遞是一維傳的,是在一條線上傳的.
//一個紅石訊號源可以讓週期d=(15-m)*2+1個紅石達到能量m及以上。
//貪心的放。結果需要的紅石為;ceil(數軸的長度/d)
int n,m,x,y,length=1,ans;
cin>>n>>m;
int d=(15-m)*2+1;
pair<int,int> last;
for(int i=1;i<=n;i++){
cin>>x>>y;
if(i>=2) length+=abs(last.first-x)+abs(last.second-y);
last={x,y};
}
//ans=length/d+length%d?1:0; //運算順序問題 wrong!!
//ans=length/d+(length%d?1:0);
ans=ceil(1.0*length/d);
if(m==0) cout<<"0"; //特判
//else cout<<ans;
//else cout<<(int)ceil(1.0*length/d); //ok
//else cout<<ceil(1.0*length/d); //返回值型別不是int,輸出會帶小數點 wrong!! 加一個fixed<<setprecision(0)才行
else cout<<fixed<<setprecision(0)<<ceil(1.0*length/d);
}
補題4:幸運號碼
題意:
做法:狀態壓縮,這種題也是一種型別吧。類似智乃的36倍數。但是這題還簡單一點,不用考慮數字的性質,只要求各位和即可。提前預處理每個數字的位數奇偶和各位之和,存在cnt[2][100]中,便於後面直接從cnt中進行查詢符合的個數。
再對每一個數字進行切割處理,用vct儲存每一個合法的切法,切完之後所需的那個數值。之後再遍歷一次vct裡面的數,在cnt中查詢符合的個數。全部加起來即是ans.
int ans=0;
int cnt[2][100]; //1奇 0偶--二維為各位之和。預處理好cnt陣列,方便查詢
vector<pair<int,int>> vct; //存放每個數字剪了之後,需要拼在左邊或右邊的數值及奇偶性
//這題因為拆位有問題,改了很久。還是不夠熟悉,寫的不夠多,一位一位拆右邊的位寫的多。一位一位拆左邊就明顯不夠熟練了,出現各種問題而且還沒一下意識到錯誤。導致改了兩個小時..
//拆右邊的數字是取餘'%',拆左邊的數字是除法'/'
void solve(){ //補L2-3幸運號碼--!!狀態壓縮!!--字串!拼接!--對於每一個數字,它可能作為左部出現,也可能作為右部出現.切割的點也可能有多個.
int n,x;
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
int digit=0,sum=0,x0=x;
while(x0){
digit++;
sum+=x0%10;
x0/=10;
}
cnt[digit&1][sum]++;
if(digit==1) vct.emplace_back(x,1);
else {
x0=x;
int cnt0=0,leftsum=sum,rightsum=0;
while(leftsum>rightsum){
vct.emplace_back(leftsum-rightsum,digit&1);
leftsum-=x0%10;
rightsum+=x0%10;
x0/=10;
}
//int num=10*(digit-cnt0-1); wa!!!!
int num=pow(10,digit-cnt0-1);
x0=x,cnt0=0,leftsum=0,rightsum=sum; //這裡只是一位,不用各位相加!!! 細節!!
bool check=false;
while(rightsum>0&&rightsum>leftsum){ //作為右部和作為左部的程式碼是不一樣的!!細節非常不一樣!!!
if(check) vct.emplace_back(rightsum-leftsum,digit&1); //第一個不放入,因為上面已經放入過了..
check=true;
num=pow(10,digit-cnt0-1);
if(num==0) break;
int cursum=0,xx0=x0/num;
while(xx0){
cursum+=xx0%10;
xx0/=10;
}
leftsum=cursum; //作為右部的細節和左部的不一樣!!!改了兩個小時....
rightsum=sum-cursum;
cnt0++;
//leftsum+=x0/num; //這裡還是有問題!!例如22222,22222/1000=22,只是得到了數字,而不是2+2=4;
//rightsum-=x0/num;
}
}
}
for(auto v:vct) ans+=cnt[v.second][v.first];
cout<<ans;
}
補題5:噁心的廣告
題意:
做法:賽時沒時間看題目,實際上不難。bfs+優先佇列即可。
注意的點:不管當前打不打得過,都要放到優先佇列裡面。優先佇列要過載運算子。power小的放在隊頂。遇到第一個打不過的就可以break了。
而且如果遇到的是強化劑,應該馬上加到ability。怎麼過載運算子也是一個點☝️,而且這個是跟平時寫的cmp是反著的。return a.power > b.power; 才是小的在堆頂.
//struct pair_hash //unordered_map存放pair需要傳入雜湊函式
//{
// template<class T1, class T2>
// std::size_t operator() (const std::pair<T1, T2>& p) const
// {
// auto h1 = std::hash<T1>{}(p.first);
// auto h2 = std::hash<T2>{}(p.second);
// return h1 ^ h2;
// }
//};
//unordered_map<pair<int,int>,int,pair_hash> mp;
int n,m,x0,cc,t,ability=-1;
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
vector< vector<int> > vct;
map<pair<int,int>,int> mp;
map<pair<int,int>,int> vis;
typedef struct myp{
int xx,yy;
int power;
friend bool operator < (const myp &a,const myp &b){ //過載運算子
return a.power>b.power; //power從小到大排序,和cmp是反著的!!!
}
}myp;
priority_queue<myp> pq;
void solve(){ //補L2-4噁心的廣告--賽時連題目都沒看,沒時間看,也沒什麼人過這題--賽後一眼bfs+優先佇列,寫寫看...
cin>>n>>m>>x0>>cc>>t;
for(int i=0;i<n;i++){
vector<int> cur;
for(int j=0;j<m;j++){
int z;
cin>>z;
cur.emplace_back(z);
}
vct.emplace_back(cur);
}
for(int i=1;i<=t;i++){
int a,b;
cin>>a>>b;
mp[{a-1,b-1}]=1;
}
myp cur;
cur.xx=x0-1,cur.yy=cc-1,cur.power=vct[x0-1][cc-1];
pq.emplace(cur);
vis[{x0-1,cc-1}]=1; //是x0-1,cc-1,而不是x0,cc
while(pq.size()){
cur=pq.top();
pq.pop();
if(ability==-1) ability=cur.power;
else if(ability>=cur.power&&!mp[{cur.xx,cur.yy}]) ability+=cur.power;
else if(!mp[{cur.xx,cur.yy}]) break;
//有可能出現一種情況就是:到了一個點,上下左右。發現都打不過,再去看別的點,別的點有打得過點,提升了能力。但是已經回不去當時打不過那些點了。
//怎麼樣在可達都所有點中,把所有都強化劑先拿了,再去打人
//不管打不得過,全部加入到優先佇列中,取出來到時候再判!!!--
for(int i=0;i<4;i++){
int x=cur.xx+dx[i];
int y=cur.yy+dy[i];
if(x>=0&&x<n&&y>=0&&y<m&&!vis[{x,y}]){
myp zz;
zz.xx=x,zz.yy=y,zz.power=vct[x][y];
pq.emplace(zz);
vis[{x,y}]=1;
if(mp[{x,y}]) ability+=vct[x][y];
}
}
}
cout<<ability;
}