赞
踩
用栈操作构建数组 | 形成两个异或相等数组的三元组数目 | 收集树上所有苹果的最少时间 | 切披萨的方案数 |
---|---|---|---|
3分 简单 | 4分 中等 | 5分 中等 | 7分 困难 |
枚举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; } };
这题复杂度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; } };
很难直接独立求出收集每个苹果的时间,收集完这颗苹果,选择的下一个苹果不同,时间可能不同。
那么,不能直接求,我们可以考虑每条边被经过了几次。
先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; } };
求方案数,显然用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; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。