leetcode 第188場周賽

用棧操作構建數組 形成兩個異或相等數組的三元組數目
 
收集樹上所有蘋果的最少時間 切披薩的方案數
3分 簡單 4分 中等 5分 中等 7分 困難

1441 用棧操作構建數組

枚舉i,從1-n,用st記錄當前匹配到目標數組的哪個位置

如果當前i==target[st],表示匹配,進行Push操作

否則,表示不匹配,先Push,Pop,這樣就不產生影響

class Solution {
public:
    vector<string> buildArray(vector<int>& target, int n) {
        vector<string>ans;
        int st=0;
        int len=target.size();
        for(int i=1;i<=n;i++){
            if(i==target[st]){
                ans.push_back("Push");
                st++;
            }
            else{
                ans.push_back("Push");
                ans.push_back("Pop");
            }
            if(st==len) break;
        }
        return ans;
    }
};

1442 形成兩個異或相等數組的三元組數目

這題複雜度O(N^2)的話,就很簡單,符合這道題的定位,要優化到O(n)的話還是有點困難的。

  • O(N^2)的做法

要使(i~j-1這一段連續異或結果)等於(j~k這一段連續異或結果)

我們可以枚舉分界點j,然後用map記錄以j爲右端點的所有子段異或情況(代碼中,用i表示分界點)

for(int j=i-1;j>=0;j--){
   tmp^=arr[j];
   mp[tmp]++;
}

然後再枚舉以i爲左端點的情況,如果此時右端點爲j,i-j的異或結果爲tmp,那麼這部分的貢獻就是mp[tmp]

for(int j=i;j<n;j++){
   tmp^=arr[j];
   ans+=mp[tmp];
}
class Solution {
public:
    int countTriplets(vector<int>& arr) {
        int n=arr.size();
        map<int,int>mp;
        int ans=0;
        for(int i=1;i<n;i++){
            int tmp=0;
            mp.clear();
            for(int j=i-1;j>=0;j--){
                tmp^=arr[j];
                mp[tmp]++;
            }
            tmp=0;
            for(int j=i;j<n;j++){
                tmp^=arr[j];
                ans+=mp[tmp];
            }
        }
        return ans;
    }
};
  • O(N)的做法

三元組表示其實就是一段區間

需要轉化下題目信息

根據題目的定義

a = arr[i] ^ arr[i + 1] ^ ... ^ arr[j - 1]

b = arr[j] ^ arr[j + 1] ^ ... ^ arr[k]

當a==b成立時

能得出一個條件a^b=0; 式子(1)

 

再觀察

x=arr[1]^arr[2]^...^arr[i]

y=arr[1]^arr[2]^...^arr[i]^arr[i+1]^...^arr[j]

若x==y,則arr[i+1]^...^arr[j]=0(一個數只有異或上0纔等於本身)

回頭看式子(1),i+1其實就是a的左端,j就是b的右端

對應三元組(i,j,k)裏的i和k

所以我們可以用map來記錄累積異或值相同時的下標,然後對於三元組(i,j,k),j可以取遍 i+1,⋯,k-1,k 共 k−i種情況。

得到初步的N^2代碼

for(int i=0;i<index.size()-1;i++)
{
    for(int j=i+1;j<index.size()-1;j++)
        ans += index[j]-(index[i]+1);
}

index表示相同累計異或值的下標

兩層for循環枚舉三元組的左右端點i和k(代碼中變量設置成了i和j)

 

到這步,優化到O(n)就很容易了

對於每個i,需要減去(n-i-1)個(index[i]+1)

需要加上index[i+1],index[i+2]...index[n],這一部分用前綴和求一下即可

這樣,就把第二個for循環優化掉了

class Solution {
public:
    int countTriplets(vector<int>& arr) {
        map<int,vector<int>>mp;
        mp[0].push_back(0);
        int tmp=0;
        for(int i=0;i<arr.size();i++){
            tmp^=arr[i];
            mp[tmp].push_back(i+1);
        }
        int ans=0;
        for(auto it:mp){
            vector<int>&index=it.second;
            vector<int>sum(index);
            int n=index.size();
            for(int i=1;i<n;i++) sum[i]+=sum[i-1];
            for(int i=0;i<n;i++){
                ans+=sum[n-1]-sum[i]-(n-i-1)*(index[i]+1);
            }
        }
        return ans;
    }
};

 


1443 收集樹上所有蘋果的最少時間

很難直接獨立求出收集每個蘋果的時間,收集完這顆蘋果,選擇的下一個蘋果不同,時間可能不同。

那麼,不能直接求,我們可以考慮每條邊被經過了幾次。

先dfs求出以0爲根,每個節點的高度dep[u]

先假設每個蘋果都是從根開始過來收集的,那麼收集每個蘋果的時間就是dep[u]*2(根->蘋果,蘋果->根)

對於每個節點u,和它的一個兒子節點v,u-v邊記爲x

如果v的子樹裏只有一個蘋果節點,那麼x這條邊確實經過了兩次,不需要進行處理;

如果v的子樹裏有兩個蘋果節點A和B,先到A,那麼A肯定是先到B,B再經過x邊回到根,該情況下x邊就多加了兩次(來一次,去一次)。

類推,對於子樹有多個蘋果節點,需要減去

max(0,(num[v]-1))*2;(num[v]是v子樹的蘋果節點個數)

const int maxn=100050;
class Solution {
public:
    vector<int>son[maxn];
    int dep[maxn],num[maxn],ans;
    void dfs(int u,int fa,vector<bool>&hasApple){
        dep[u]=dep[fa]+1;
        if(hasApple[u-1]){
            num[u]=1;
            ans+=dep[u]*2;
        }
        else num[u]=0;
        for(auto v:son[u]){
            if(v==fa) continue;
            dfs(v,u,hasApple);
            ans-=max(0,(num[v]-1))*2;
            num[u]+=num[v];
        }
    }
    int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
        ans=0;
        dep[0]=-1;
        for(auto it:edges){
            son[it[0]+1].push_back(it[1]+1);
            son[it[1]+1].push_back(it[0]+1);
        }
        dfs(1,0,hasApple);
        return ans;
    }
};

 


1444 切披薩的方案數

求方案數,顯然用dp求

因爲每次都會留下右下部分,所以每次切的時候,相當於選擇了一個矩形的左上角

dp[i][j][l]
dp數組意義:以(i,j)爲左上角,已經分成l次的方案數
所以對於每個狀態(i,j,l),我們去枚舉下一切的左上角作爲下一個狀態去轉移
1)當水平切的時候
    dp[ii][j][l+1]=(dp[ii][j][l+1]+dp[i][j][l])%mod;
ii是水平切的行座標
2)當豎直切的時候
    dp[i][jj][l+1]=(dp[i][jj][l+1]+dp[i][j][l])%mod;
jj是豎直切的列座標
​
有了轉移方程,我們現在要考慮題目裏的另一個限制條件,每一塊分區都必須至少有一個蘋果,也就是說只有滿足條件纔會發生狀態轉移
一個矩形內有無蘋果,可以把有蘋果的點記爲1,其他記爲0
​
然後前綴和差分來求出一個矩形是否含有1,關於前綴和差分,有不清楚的可以搜博客學習一下
​

 

const long long mod=1e9+7;
class Solution {
public:
    int ways(vector<string>& pizza, int k) {
        int row=pizza.size();
        int col=pizza[0].size();
        long long sum[55][55],dp[55][55][15];
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(pizza[i-1][j-1]=='A');
            }
        }
        dp[1][1][1]=1;
        long long ans=0;
        for(int i=1;i<=row;i++){
            for(int j=1;j<=col;j++){
                for(int l=1;l<=k;l++){
                    if(!dp[i][j][l]) continue;
                    //水平切
                    for(int ii=i+1;ii<=row;ii++){
                        int flag=1;
                        if(sum[ii-1][col]-sum[ii-1][j-1]-sum[i-1][col]+sum[i-1][j-1]) flag&=1;
                        else flag=0;
                        if(sum[row][col]-sum[row][j-1]-sum[ii-1][col]+sum[ii-1][j-1]) flag&=1;
                        else flag=0;
                        if(flag){
                            dp[ii][j][l+1]=(dp[ii][j][l+1]+dp[i][j][l])%mod;
                        }
                    }
                    //豎直切
                    for(int jj=j+1;jj<=col;jj++){
                        int flag=1;
                        if(sum[row][jj-1]-sum[row][j-1]-sum[i-1][jj-1]+sum[i-1][j-1]) flag&=1;
                        else flag=0;
                        if(sum[row][col]-sum[row][jj-1]-sum[i-1][col]+sum[i-1][jj-1]) flag&=1;
                        else flag=0;
                        if(flag){
                            dp[i][jj][l+1]=(dp[i][jj][l+1]+dp[i][j][l])%mod;
                        }
                    }
                }
                ans=(ans+dp[i][j][k])%mod;
            }
        }
        return ans;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章