寒假訓練第一場-創(chuàng)新互聯(lián)

一:前綴和 1.題目:數圈圈 題目鏈接:數圈圈 解題思路:

題目給出兩個數字a, b, 讓你求出 a - b 范圍內每個數字”圈“的總和。 對于這樣的區(qū)間和問題,直接預處理出前綴和。查詢即可

創(chuàng)新互聯(lián)建站-專業(yè)網站定制、快速模板網站建設、高性價比華龍網站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式華龍網站制作公司更省心,省錢,快速模板網站建設找我們,業(yè)務覆蓋華龍地區(qū)。費用合理售后完善,十多年實體公司更值得信賴。代碼塊:
`#includeusing namespace std;
const int N = 1e6 + 10;

int cir[10] = {1, 0, 0, 0, 1, 0, 1, 0, 2, 1};
int f[N];

int main()
{int n;
    cin >>n;
    
    //根據數據范圍,預處理出 1 - 1e6 的前綴和數組
    for(int i = 1; i<= 1000000; i++)
    {int x = i;
        int sum = 0;
        while(x)
        {sum += cir[x % 10];
            x /= 10;
        }   
        f[i] += f[i - 1] + sum; 
    }
    for(int i = 0; i< n; i++)
    {int a, b;
        cin >>a >>b;
        cout<< f[b] - f[a - 1]<< endl;
    }
    return 0;
    }
2.題目:珂朵莉與宇宙 題目鏈接:珂朵莉與宇宙 解題思路:

假設暴力處理,那么需要枚舉所有的子區(qū)間內的和,枚舉區(qū)間和用前綴和,并枚舉理論上符合的 j^2。
即為, s[r] - s[l] = j^2,枚舉三個變量肯定超時。
通過變化式子
s[r] - j^2 = s[l], 那么只需要枚舉 r, 和 j, 和符合的 s[l], 的個數,因為s[l], 可以循環(huán) r 時,一并維護出,所以優(yōu)化掉一個枚舉。

代碼塊:
#include#includeusing namespace std;
#define ll long long
const int N=1e6+10;
ll a[N];
ll cn[N];
int main(){ ll n;cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)a[i]+=a[i-1];
    //mapma;
    cn[0]=1;
    ll sum=0;
    for(int i=1;i<=n;i++){for(int j=0;j*j<=a[i];j++){sum+=cn[a[i]-j*j];
        }
        cn[a[i]]++;
    }
    cout<
3.題目:激光炸彈 題目鏈接:激光炸彈 解題思路:

二維前綴和,因為我們要枚舉很多子區(qū)間,觀察數據范圍,如果枚舉子區(qū)間會超時。對于二維區(qū)間和的問題,我們使用二維前綴和。
不會二維前綴和可以參考以下文章
二維前綴和

代碼塊:
#includeusing namespace std;
const int N = 5010;

int g[N][N];

int main()
{ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    int n, m;
    cin >>n >>m;

    for(int i = 0; i< n; i++)
    {int a, b, c;
        cin >>a >>b >>c;
        a++, b++;
        g[a][b] = c;

    }
    
    for(int i = 1; i< N; i++)
        for(int j = 1; j< N; j++)
        {g[i][j] = g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1] + g[i][j];
        }
    

    int ans = 0;

    for(int i = m; i< N; i++)
        for(int j = m; j< N; j++)
        {int x2 = i, y2 = j;
            int x1 = i - m + 1, y1 = j - m + 1;

            ans = max(ans, g[x2][y2] - g[x1 - 1][y2] - g[x2][y1 - 1] + g[x1 - 1][y1 - 1]);
        }

    cout<< ans;


    return 0;
}
二:差分 1.題目:校門外的樹 題目鏈接:校門外的樹 解題思路:

首先觀察數據范圍,考慮暴力是否可做,可做,這里不提供暴力解法。
這個題推薦使用差分來做,來練習差分的使用。
差分可以實現對一個區(qū)間的所有數,加減一個數字。對此題,我們假設 0 代表有樹木,對一段區(qū)間砍樹就規(guī)定為對一段區(qū)間的數 -1。
這樣我們就減少了去遍歷區(qū)間內的每個數字進行減法,而在最后所有操作進行完畢之后,統(tǒng)一計算每個點的值。如果還是0,那么還有樹。

代碼塊:
#includeusing namespace std;

const int N=1e4 + 10;

int f[N];
int n, m;

int main()
{cin >>n >>m;
    
    for(int i = 0; i< m; i++)
    {int a, b;
        cin >>a >>b;
        f[a]--;
        f[b + 1]++;
    }

    int ans = 0;

    for(int i = 0; i<= n; i++)
    {f[i] += f[i - 1];
        if(!f[i]) ans++;
    }

    cout<< ans<< endl;


    return 0;
}
2.題目:貨物種類 題目鏈接:貨物種類 解題思路:

不難發(fā)現這個不同于以前的統(tǒng)計個數嗎,而是統(tǒng)計不同種類的數量。所以我們不能對個數進行維護差分數組。
所以我們要對不同的種類維護差分數組,所以我們需要一個一個種類的進行加。并且重復區(qū)間不能重復計數,需要區(qū)間合并。
所以我們先結構體存下來,進行排序。
因為我們要進行區(qū)間合并,所以盡可能 l 的小的排在前面。
區(qū)間合并時:分三種情況。

對于同一個種類的區(qū)間合并:
1.如果說下一個區(qū)間的 l 大于當前維護的區(qū)間的 r, 說明這倆區(qū)間沒有交集,那么 維護區(qū)間數組,更新維護的區(qū)間
2.否則當前l(fā) 小于等于當前維護區(qū)間的 r,說明他倆有交集,如果說當前 r >當前維護的區(qū)間,那么維護區(qū)間 r 需要擴大了。

遇到不同種類了
把之前維護的區(qū)間更新差分數組,然后更新區(qū)間

代碼塊:
#includeusing namespace std;
const int N = 1e5 + 10;


int f[N];

struct Node
{int l, r, id;
}e[N];

bool bmp(Node x, Node y)
{if(x.id != y.id)
    return x.id< y.id;
    else if(x.l != y.l)return x.l< y.l;
    else return x.r< y.r;
}

int main()
{int n, m;
    cin >>n >>m;

    for(int i = 1; i<= m; i++) cin >>e[i].l >>e[i].r >>e[i].id;

    sort(e + 1, e + m + 1, bmp);

    int st = e[1].l, ed = e[1].r;
    int last = e[1].id;

    for(int i = 2; i<= m; i++)
    {if(last != e[i].id)
        {last = e[i].id;
            f[st]++, f[ed + 1]--;
            st = e[i].l, ed = e[i].r;
        }else
        {if(e[i].l >ed)
            {f[st]++, f[ed + 1]--;
                st = e[i].l, ed = e[i].r;
            }else if(ed< e[i].r) ed = e[i].r;
        }
    }


    f[st]++, f[ed + 1]--;

    int mxx = 0;
    int ans = 0;

    for(int i = 1; i<= n; i++)
    {f[i] += f[i - 1];
        if(f[i] >mxx)
        {mxx = f[i];
            ans = i;
        }
    }


    cout<< ans<< endl;




    return 0;
}
3.題目:放學后茶會的甜點 題目鏈接:[放學后茶會的甜點] 解題思路:

優(yōu)先學習二維差分:

二維差分
二維差分預處理題目對子區(qū)間增加 1。題目問取任意大小的區(qū)間的大平均甜度,既然是平均甜度,所以我們取大值那一個即可。
因為平均值不會超過大值。

代碼塊:
#includeusing namespace std;

const int N = 1010;

int g[N][N];

int main()
{int n, m, t;
    cin >>n >>m >>t;

    for(int i = 1; i<= t; i++)
    {int x1, y1, x2, y2;
        cin >>x1 >>y1 >>x2 >>y2;

        g[x1][y1] += 1;
        g[x2 + 1][y1] -= 1;
        g[x1][y2 + 1] -= 1;
        g[x2 + 1][y2 + 1] += 1;

    }

    int ans = 0 ;
    
    for(int i = 1; i<= n; i++)
        for(int j = 1; j<= m; j++)
    {g[i][j] += g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1];
        
        ans = max(ans, g[i][j]);
    }


    cout<< ans<
三:貪心 1.題目:小杰的簽到題 題目鏈接:小杰的簽到題 解題思路:

首先不希望桌子有空閑時間,所以誰先到誰就先排隊。
所以我們先排序所有人的到達時間,從到達時間小的開始放置。
假設對于我們當前要放置的隊伍來說,肯定放在最先結束的桌子上。找到三個桌子誰先結束。所以我們需要維護每個桌子當前結束時間。

代碼塊:
void solved()
{cin >>n;
    for(int i = 0; i< n; i++) cin >>a[i];
    cin >>m;
    
    //初始化桌子,時間都歸0
    for(int i = 0 ;i< 3; i++)
        que[i] = 0;
    
    //排序讓先到的進行
    sort(a, a + n);

    for(int i = 0; i< n; i++)
    {int x = a[i];
        int mnn = 1e9; // 找到三個桌子誰最先結束
        int xb = 0;    //記錄是第幾個桌子
        for(int j = 0; j< 3; j++)
        {if(que[j]< mnn) mnn = que[j], xb = j;
        }
        
        //如果說當前隊伍大于等于最先結束的了,那么肯定按照當前隊伍到達的時間來開始
        if(x >= mnn)
        {que[xb] = x + m;
        }else que[xb] = mnn + m; 
        //如果我們到達的時候還沒有空桌子,那么肯定等空桌子結束我們開始

    }
    
    //三個桌子找到最晚結束的就是我們的答案
    int ans = 0;
    for(int i = 0; i< 3; i++)
    {ans = max(ans, que[i]);
    }

    cout<< ans<< endl;

}
2.題目:排座椅 題目鏈接:排座椅 解題思路:

首先題目中說所有說話的人是相鄰的(上下左右), 對于豎著相鄰的人來說,橫著的過道才能影響,對于橫著相鄰的來說,豎著的過道影響。
對于一條豎著的過道,影響的只能是相鄰兩列的人,所以貪心的來說,對于每一條過道我們都希望是影響最多的人。
那么統(tǒng)計一下每對人如果要拆開,需要在哪一列 / 一行,防止過道。然后排序從大到小,輸出題目要求的過道數量即可。
需要注意的點,題目要求輸出過道的位置時,要求從小到大輸出。
因為我們需要輸出哪個過道被統(tǒng)計的數量最多,所以我們在統(tǒng)計次數的時候,也要標記上是哪個過道,這樣排序的時候我們知道是哪個過道最多。
所以使用結構體存。

代碼塊:
#includeusing namespace std;
const int N = 2010;
int hs[N], ls[N];

struct Node
{int id, w;
}he[N], le[N]; //分別統(tǒng)計行和列的次數


bool bmp(Node x, Node y)
{return x.w >y.w;
}


int main()
{int n, m, k, l, d;
    cin >>n >>m >>k >>l >>d;

    for(int i = 0; i< d; i++)
    {int x1, y1, x2, y2;
        cin >>x1 >>y1 >>x2 >>y2;
        
        //上下相鄰
        if(x1 == x2)
        {he[min(y1, y2)].w++;
            he[min(y1, y2)].id = min(y1, y2); //避免排序時候不知道是哪個過道
        }else if(y1 == y2) //左右相鄰
        {le[min(x1, x2)].w++;
            le[min(x1, x2)].id = min(x1, x2);
        }
    }
    
    //按照統(tǒng)計次數,從大到小
    sort(he, he + N, bmp), sort(le, le + N, bmp);

    
    //因為題目要求輸出的時候按照 過道的下標從小到大
    vectorans1, ans2;

    for(int i = 0; i< k; i++)
        ans1.push_back(le[i].id);

    for(int i = 0; i< l; i++)
        ans2.push_back(he[i].id);
        
        
    sort(ans1.begin(), ans1.end()), sort(ans2.begin(), ans2.end());

    for(int i = 0; i< ans1.size(); i++)
        cout<< ans1[i]<< " ";
    cout<< endl;
    for(int j = 0; j< ans2.size(); j++)
        cout<< ans2[j]<< " ";


    return 0;
}
3.題目:紀念品分組 題目鏈接:紀念品分組 解題思路:

題目規(guī)定,最多 2 個物品一組,并且組內大物品不能超過上限值,問你最少多少組。
我們肯定希望盡可能滿足 2 個物品一組,所以我們讓最小的價值和大的價值組一塊是最優(yōu)解。如果說不能組一塊,那大價值只能自己一組。
那么最小值就依次找次小值。

代碼塊:
#includeusing namespace std;
const int N = 30010;

int n, m;
int a[N];

int main()
{cin >>m >>n;

    for(int i = 0; i< n; i++) cin >>a[i];

    sort(a, a + n);

    int l = 0;
    int ans = 0;

    for(int i = n - 1; i >= l; i--)
    {if(i == l)
        {ans++;
            break;
        }

        if(a[i] >= m) ans++;
        else
        {if(a[i] + a[l] >m)
            {ans++;
            }else
            {ans++;
                l++;
            }
        }
    }

    cout<< ans;

    return 0;
}
4.題目:巨石滾滾 題目鏈接:巨石滾滾 解題思路:

需要注意的點,土球會先撞,再回復,如果說撞的時候就< 0, 那么就失敗了。

首先我們可以把所有的障礙分為兩類,一類是撞完會增加的,另一類是撞完會減少的。
因為我們希望盡可能的把球的穩(wěn)定性變成大,然后再減小,這樣盡可能滿足更多的障礙。
所以我們先撞增加的,再撞減小的。

對于先撞增加的這一類:
因為先撞再增加,所以盡可能的使得撞的值小的放在前面,讓球的穩(wěn)定性盡可能大,避免撞完就小于0。

可能會說,存在一個增加的障礙,但是他的損耗是很大的,肯定會導致撞完就小于0,不能回復。
這樣因為他是撞完增加的,我們把他放在前面,是不是最優(yōu)解?。
因為我們的要求是能夠到達終點,而不是通過最多的障礙。
因為這個是損耗大的回復,所以我們肯定把他放在這一類的最后來撞,因為這時已經是極大值的穩(wěn)定性,如果這時都不能通過,所以肯定不能到達終點。

對于撞完損耗這一類:
因為我們目標是到重點,那么如果到終點,我們大值 + 回復 一定大于所有的損耗 (損耗是一個定值),否則肯定會在路上失敗。
那么我們盡可能的讓回復多的在前面,這樣就盡可能的滿足穩(wěn)定性大。

代碼塊:
#includeusing namespace std;

typedef pairPII; //typedef 同define, pair用法 csdn搜索

#define x first
#define y second


const int N = 500010;

int n, m;

bool bmp1(PII a, PII b)
{return a.x< b.x;
}

bool bmp2(PII a, PII b)
{return a.y >b.y;
}

PII all[N];

void solved()
{cin >>n >>m;

    vectorzh, fu;

    for(int i = 0; i< n; i++)
    {cin >>all[i].x >>all[i].y;

        if(all[i].y - all[i].x >= 0) zh.push_back(all[i]);
        else fu.push_back(all[i]);
    }

    sort(zh.begin(), zh.end(), bmp1);
    sort(fu.begin(), fu.end(), bmp2);

    long long sum = m;

    for(int i = 0; i< zh.size(); i++)
    {if(sum - zh[i].x< 0)
        {cout<< "No"<< endl;
            return;
        }else sum -= zh[i].x, sum += zh[i].y;
    }

    for(int i = 0; i< fu.size(); i++)
    {if(sum - fu[i].x< 0)
        {cout<< "No"<< endl;
            return;
        }else sum -= fu[i].x, sum += fu[i].y;
    }

    cout<< "Yes"<< endl;
}
int main()
{int T;
    cin >>T;
    while(T--) solved();

    return 0;
}
四:二分&二分答案 1.題目:.完全平方數 題目鏈接:完全平方數 解題思路:

暴力的解法就是 從 左區(qū)間l到右區(qū)間r 找到滿足條件數的個數。但考慮到 l和r的大小 可以先預處理出來所有的平方數,然后用二分查找到 處于區(qū)間(l,r)滿足條件數的范圍。即 查找 l和r所在預處理數組中的位置。

代碼塊:
#include#includeusing namespace std;
#define ll long long
vectorv;
void solve(){ll l1,r1;cin>>l1>>r1;
    ll l=0,r=v.size()-1;
    ll k1=-1;
    ll k2=-1;
    //找到第一個 大于等于 l1的
    while(lll mid=(l+r)>>1;
        if(v[mid]>=l1)r=mid;
        else l=mid+1;
    }
    k1=l;
    //找到最后一個 小于等于r1的
    l=0,r=v.size()-1;
    while(lll mid=(l+r+1)>>1;
        if(v[mid]<=r1)l=mid;
        else r=mid-1;
    }
    k2=l;
    cout<for(ll i=0;i*i<=1e9;i++){v.push_back(i*i);
    }
    ll t;cin>>t;
    while(t--)solve();
}
2.題目:解方程 題目鏈接:解方程 解題思路:

可以使用二分答案 枚舉區(qū)間(0,100)內的所有數字,注意寫好check函數就可以了。這里可以學習經典 浮點數二分的處理方法。

代碼塊:
#include#include#define ll long long
using namespace std;
double n;
double check(double m){ double s=m*m*m*m*2018+m*21+5*m*m*m+5*m*m+14;
    return s;
}
void solve(){cin>>n;
    if(check(100)cout<<"-1"<0.00001){double mid=(l+r)/2.0;
        if(check(mid)>=n)r=mid;
        else l=mid+0.00000001;
    }
    printf("%.4f\n",l);
}
int main(){ll t;cin>>t;
    while(t--){solve();
    }
    return 0;
}
3.題目:跳石頭 題目鏈接:跳石頭 解題思路:

使用二分答案,通過枚舉 答案 即最短跳躍距離的大值,check()函數是判斷該答案是否可行,是通過 計算 在滿足該條件下需要移走巖石的個數,是否滿足題目限制的大移動巖石個數。

代碼塊:
#include#include#include#include
#include#include#include#include#define ll long long
const double eps = 1e-4;
ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a; }//最小公約數__gcd()
using namespace std;
const int NX=1e5+10;
ll x[NX];
ll y[NX];
ll L, N, M;
bool check(ll k)
{ll sum = 0;
    for (int i = 1; i<= N; i++)
    {ll j = i - 1;
        while (y[j] != 0)j--;
        if ((x[i] - x[j])sum++;
            y[i] = 1;
            if (sum >M)return false;
        }
    }
    ll s = N;
    while (y[s] != 0)s--;
    if ((x[N + 1] - x[s])< k)sum++;
    if (sum >M) return false;
    else return true;
}
int main()
{cin >>L >>N >>M;
    for (int i = 1; i<= N; i++)
    {cin >>x[i];
    }
    x[0] = 0;
        x[N + 1] = L;
    ll l = 0;
    ll r = 1e9+10;
    while (l< r)
    {memset(y, 0, sizeof(y));
        ll mid = (l + r) >>1;
        if (check(mid))l =mid+1;
        else r = mid;
    }
    cout<< l-1<< endl;
    return 0;
}
4.題目:借教室 題目鏈接:[借教室] 解題思路:

該題目 相當于是 跳石頭 的一個拓展,同樣是枚舉答案 需要通知哪個申請人修改訂單。注意check()函數的書寫,在判斷答案 是否可行的時候 需要用到 一點差分的知識。 使用差分處理完該申請人 之前的所有訂單,然后看是否有不符合要求的情況,即供不應求(需要的教室個數 大于 有空閑的教室個數。

代碼塊:
#include#include#includeusing namespace std;
#define ll long long
const int N=1e6+10;
ll n,m;
ll a[N],b[N],c[N],d[N],e[N]={0};
bool check(int k)
{memset(e,0,sizeof(e));
   for(int i=1;i<=k;i++)
   {   e[c[i]]+=b[i];
       e[d[i]+1]-=b[i];
   }
    for(int i=1;i<=n;i++)
    {e[i]+=e[i-1];
        if(e[i]>a[i])return false;
    }
    return true;
}
int main()
{cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++)cin>>b[i]>>c[i]>>d[i];
    ll l=1,r=m;
    while(l  ll mid=(l+r)>>1;
       if(check(mid))l=mid+1;
        else r=mid;
    }
    if (l != m){cout<<"-1"<
五:雙指針算法 1.題目:滑動窗口 題目鏈接:滑動窗口 解題思路:

雙指針經典用法,需要 用兩個指針維護一個窗口的長度,然后不停的在一個數組上滑動。首先 需要注意,兩個指針必須一起移動,并且 該窗口的長度不會改變,所以 可以使用 滑動窗口去 維護 給定區(qū)間長度 的最值。

代碼塊:
#include#include 
#include#include#include#include#include#includeusing namespace std;
#define ll long long
const int N = 1e6+10;
ll p[N], d[N]={0};
ll a[N];
ll q[N];
int main()
{ll n, k; cin >>n >>k;
	ll be = 0, ed =-1;
	for (int i = 1; i<= n; i++)cin >>a[i];
    // be 和 ed 就是 維護 窗口 長度 的兩個指針 
	for (int i = 1; i<= n; i++)
	{// 當窗口 長度大于給定值的時候 就將左端點++ 減小窗口長度 維護窗口長度
		if (be<= ed && i - k + 1 >q[be])be++;
        // 維護 窗口的最值 保證最值一定在 數組的最左邊 即 be位置
		while (be<= ed && a[q[ed]] >= a[i])ed--;
		q[++ed] = i;
		if (i >= k)printf("%lld ", a[q[be]]);
	}
    cout<// 當窗口 長度大于給定值的時候 就將左端點++ 減小窗口長度 維護窗口長度
		if (be<= ed && i - k + 1 >q[be])be++;
        // 維護 窗口的最值 保證最值一定在 數組的最左邊 即 be位置
		while (be<= ed && a[q[ed]]<= a[i])ed--;
		q[++ed] = i;
		if (i >= k)printf("%lld ", a[q[be]]);
	}
    cout<
2.題目:逆序數(歸并排序) 題目鏈接:[逆序數] 解題思路:

首先我們知道逆序數的定義,然后發(fā)現通過 歸并排序 的過程,可以計算逆序數,然后 歸并排序 其實就是通過遞歸 在回溯的過程中(通過定義 發(fā)現 回溯過程返回的都是 有序數組),,使用 雙指針 有序合并兩個有序數組。

代碼塊:
#includeusing namespace std;
#define ll long long
const int N=1e5+10;
ll q[N],tmp[N];
ll ans=0;
void merge_sort(ll q[], ll l, ll r)
{if (l >= r) return;

    ll mid =( l + r) >>1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);

    ll k = 0, i = l, j = mid + 1;
    while (i<= mid && j<= r)
        if (q[i]<= q[j]) tmp[k ++ ] = q[i ++ ];
        else {tmp[k ++ ] = q[j ++ ];ans+=mid-i+1;}

    while (i<= mid) tmp[k ++ ] = q[i ++ ];
    while (j<= r) tmp[k ++ ] = q[j ++ ];

    for (i = l, j = 0; i<= r; i ++, j ++ ) q[i] = tmp[j];
}
int main(){ll n;cin>>n;
    for(int i=1;i<=n;i++)cin>>q[i];
    merge_sort(q, 1, n);
    cout<
六:遞歸&分治 1.題目:數的計算 題目鏈接:數的計算 解題思路:

首先使用暴力 可以發(fā)現f(i)=f(1)+f(2)+…+f(i/2)然后寫一個遞歸函數就可以
但是 發(fā)現題目數據 發(fā)現這樣 暴力過不了,因為 我們做了很多重復的計算,比如 當i為奇數的時候 它能添加數字的情況跟(i-1)添加數字的情況一樣,當i為偶數的時候,就相當于在(i-1)的基礎上加上了 i可添加的自然數的情況,即(i/2)的情況一樣。得到普遍規(guī)律: 當i為奇數的時候f(i)=f(i-1) 當i為偶數的時候 f(i)=f(i-1)+f(i/2).

代碼塊:
#includeusing namespace std;
#define ll long long
ll dfs(ll k){if(k==1)return 1;
    if(k%2)return dfs(k-1);
    else return dfs(k-1)+dfs(k/2);
}
int main(){ll n;cin>>n;
   cout<< dfs(n)<
2.題目:小q的數列 題目鏈接:小q的數列 解題思路:

題目有兩個要求,第一個要求 就可以直接通過 題目給出的條件進行遞歸就可以,第二個條件 通過枚舉不難發(fā)現跟二進制有關系,每次一個新數x的出現一定是在 2的x次方-1的位置出現。

代碼塊:
#include#include#include
#include#define ll long long
using namespace std;
ll solve(ll n)
{if(n==0)return 0;
    if(n==1)return 1;
    return solve(n/2)+solve(n%2);
}
int main()
{ ll t;cin>>t;
    while(t--)
    {ll n;cin>>n;
        ll a=solve(n);
        ll p=pow(2,a)-1;
        printf("%lld %lld\n",a,p);
    }
    return 0;
}
3.題目:求先序排列 題目鏈接:求先序排列 解題思路:

我們首先 需要學習 樹的三種遍歷方式(本質計算 對根節(jié)點的訪問順序不同),先序排列(根左右) 中序排列(左根右)后序排列(左右根)先序排列的第一個是根節(jié)點,后序排列的最后一個節(jié)點是根節(jié)點。我們可以先根據后序序列找到根節(jié)點,然后根據這個根節(jié)點來對中序以及后序序列進行分割,于是可以得到左右字數,然后對左子樹以及右子樹進行遞歸地處理。

代碼塊:
#include#includeusing namespace std;
#define ll long long
void solve(string s1,string s2){ll k1=s1.size();
    ll k2=s2.size();
    if(k2<=0)return;
      cout<string s1,s2;cin>>s1>>s2;
                solve(s1,s2);
	return 0;
 }
七:STL 1.題目:掃雷游戲 題目鏈接:掃雷游戲 解題思路:

根據題意 如果該位置是非地雷格的時候 你需要 計算它周圍的地雷個數,如果是地雷格的話 就直接輸出‘*’
這里有個小技巧 在地圖上 位置的遍歷可以用 x和y相對于原來位置數值的變化 直接判斷八個方向,在代碼中用 d_x和d_y實現。

代碼塊:
#include#include#include
#include#include#include#include#define  ll  long long
using namespace std;
char a[110][110];
ll  b[110][110];
// 八個位置 x 和 y的變化 分別是 下 上 右 左 左上 左下 右下 右上
ll  d_x[10] = {0,0,1,-1,-1,-1,1,1};
ll  d_y[10] = {1,-1,0,0,-1,1,1,-1};
ll  check(int x, int y)
{ll  sum = 0;
	for (int i = 0; i< 8; i++)
	{ll t_x = x + d_x[i];
		ll t_y = y + d_y[i];
		if (a[t_x][t_y] == '*')sum++;
	}
	return sum;
}
int main()
{	ll  x, y;cin >>x >>y;
	for (int i = 1; i<= x; i++){for (int j = 1; j<= y; j++)cin >>a[i][j];
    }
    for (int i = 1; i<= x; i++){for (int j = 1; j<= y; j++)
		{// 如果該位置是非地雷格 則通過 check 函數計算旁邊位置地雷的個數
			if (a[i][j] == '?')b[i][j]=check(i, j);
		}
	}
	for (int i = 1; i<= x; i++)
	{for (int j = 1; j<= y; j++)
		{	if (a[i][j] == '*')cout<< a[i][j];
			else cout<< b[i][j];
		}
		cout<< endl;
	}
	return 0;
}
2.題目:圖書管理員 題目鏈接:圖書管理員 解題思路:

首先理解題意我們發(fā)現我們需要 找到是否能夠找到讀者需要的書,找到的話輸出 圖書編碼最小的書,反之輸出 -1,我們可以 先將圖書排序,然后每次進行查詢的時候,就遍歷一遍 如果存在就輸出(因為已經進行了排序,所以能夠保證 每次最先找到的是圖書編碼最小的書),不存在就輸出-1.

代碼塊:
#include#include#include
#include#include#include#include#define  ll  long long
using namespace std;
vectorv;
bool cmp(string &s1,string &s2)
{if (s1.size() == s2.size()) return s1< s2;
	else	return s1.size()< s2.size();
}
bool judge2(string &s, string &s1)
{// 使用 雙指針 從尾往前判斷 是否 圖書編碼 是否是以讀者的需求碼結尾
	ll a = s.size()-1;
	ll b = s1.size()-1;
	while (s[a]==s1[b])
	{a--;b--;
		if (a == -1 || b == -1)break;
	}
	if (b == -1)return true;
	else return false;
}
void judge(string &s,int n)
{for (int i = 0; i< v.size(); i++)
	{// 圖書編碼長度小于讀者的需求碼 必定不滿足,所以直接跳過
		if (v[i].size()< n)continue;
        // 圖書編碼長度等于讀者的需求碼
		else if (v[i].size() == n)
		{// 如果 圖書編碼等于讀者的需求碼
			if (v[i] == s){		cout<< v[i]<< endl;
				return;
			}
		}
		else
		{// 判讀圖書編碼 是否是 以讀者的需求碼結尾
			if (judge2(v[i], s)){		cout<< v[i]<< endl;
				return;
			}
		}
	}
	cout<< "-1"<< endl;
}
int main()
{ll n,m;cin >>n>>m;
	for (int i = 1; i<= n; i++){string s;cin >>s;
		v.push_back(s);
	}
    //對書 進行排序
	sort(v.begin(), v.end(),cmp);
	while (m--)
	{ll x;cin >>x;
		string s;cin >>s;
        // 判斷 該書是否存在
		judge(s,x);
	}
	return 0;
}

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧

網站名稱:寒假訓練第一場-創(chuàng)新互聯(lián)
分享網址:http://muchs.cn/article0/psgoo.html

成都網站建設公司_創(chuàng)新互聯(lián),為您提供網站建設標簽優(yōu)化、網頁設計公司、云服務器、App開發(fā)、定制網站

廣告

聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)