11.无向图删边问题
原题:https://ac.nowcoder.com/acm/contest/11257/C
问题:有n个顶点的无向完全连通图,求删除多少个三角形后,剩余边数可以比n小,并输出所有删除结果(有多解)。(n<2000)
算法类型:new
时间复杂度:o(n2logn)
空间复杂度:o(<n2)
算法思路:如果建立邻接矩阵,从(1,2,3)暴搜也不会t,但会wa——由于从小的点开始删,删到最后,越大结点的会剩越多的不能组成三角形的边,也就是把原来能构成三角形的边全连到了一个点上,这样造成了很大的浪费,并且最后剩余的边往往大于n,所以wa了。因此,解决这题的核心思想,就是能做到边的平均分配,那么如何做到边的平均分配且不重边而且不t?这是个很难的问题,但是我们可以从小的方面解决:如果求不同边三角形不好求,那就先求不同边,那就好求了——(1,2)(1,3)……(1,100)(设n=100),然而会发现,如果这么取了的话,就会出现上面的“一点多边”问题,没有"平均分配",要平均就可以把所有对边连起来(1,50)(2,51)……(50,100)这样可以了,那找这些有什么用呢?线都是由两个点构成的,那么我再增加一个点,不就是三角形了?如果我让n+1的点,将上面的点全连起来,不就有50个平均分配的三角形?BUT!虽然100个点平均分配了(各删2条),但是新的点n+1被删了100条线,并没平均,那么可以再增加n+2,再去删1~100(此时由于不重边,可以(2,50)(3,51)……(50,1)这么删),加到n+50,这样就都“平均分配"了,但问题又来了——下一步怎么走?反一下?把n到n+50两两连,再在1到100选点连?那么比例不够呀!那就把51到n+50两两连,在1到50选点?看起来好像没问题,但——我们确定了1到n是互相连了的,n+1也把1到n所有都连了,那么51连n+50必会存在重复边!但同时,我们可以确定n+1到n+50各点是相互独立的,所以最保守的方式就是在n+1到n+50范围去连以及取点,但是,1到100只删了一半,他们还需要连线,连接他们可能又有重边,“不安全”,要是我们能解决掉1到100所有边,把范围缩小到n+1到n+50,把问题规模缩小就好了。那能缩小吗?可以的,回到前面,如果我扩建到n+50能让他们删100,那我扩建到n+100是否可以让他们全删?也就是2n,那么这题思路就出来了:给定n个顶点,将1到n/2作为选点,将n/2+1到n作为连点,连完之后所有连点连完,剩余选点再二分,重复上述操作,最后就能得出结果。这就是从建立“平均分配”思想到打破“平均分配”得出规律的过程。
算法难点:算法主要难点是思维,有了思维成功九成,剩下的就是如何再n/2+1到n的范围每次连边不同了,北大采用了i,m+(i+j-1)%(n-m-1)+1,m+(i-j+n-m-2)%(n-m-1)+1;的方法计算,可以通过debug查看选边过程。
研究时间:2021.8.2 8h

#include<bits/stdc++.h>
//#include<windows.h>
using namespace std;vector<tuple<int,int,int>> edges;
const int N=2000+5;
bool lk[N][N];
int sum[N];
void work(int n) {if(n<=2) {return ;}if(n==3) {edges.push_back({1,2,3});return ;}int m=(n+1)/2;if((n-m)%2)--m;if(m%2&&m*2-1==n)m-=2;for(int i=1; i<=min(m,n-m-1); ++i) {edges.push_back({i,m+i,n});for(int j=1; j<(n-m)/2; ++j)edges.push_back({i,m+(i+j-1)%(n-m-1)+1,m+(i-j+n-m-2)%(n-m-1)+1});}work(m);
}
/*void Color(int col,int bac) {SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),col|0|0|bac*16);
}*/
int main() {#ifdef kczfreopen("1.in","r",stdin);//freopen("1.out","w",stdout);
#endifint n;cin>>n;for(int i=1; i<=n; i++)sum[i]=n-1;work(n);cout<<edges.size()<<endl;for(int i=1; i<=n; ++i)lk[i][i]=1;for(auto [x,y,z]:edges) {// Color(15,0);printf("%d %d %d\n",x,y,z);/*sum[x]-=2;sum[y]-=2;sum[z]-=2;for(int i=1;i<=n;i++){Color(0,sum[i]%15+1);printf("%2d ",sum[i]);}Color(15,0);printf("\n");*/lk[x][y]=lk[y][z]=lk[x][z]=lk[y][x]=lk[z][y]=lk[z][x]=1;}
}

input:

output:

debug:
各个结点剩余边数

12.统计子序列数目问题
原题:https://leetcode-cn.com/problems/count-number-of-special-subsequences/
问题:有一数组a只有012,求a中满足含012非递减子序列个数(n<105)(mod=1e9+7)
算法类型:动态规划
时间复杂度:o(n)
空间复杂度:o(n)
算法思路:如果是求子串,用双指针,求子序列,一般是用动态规划——该题既无后效性(求前面的总数,不会用到后面的数据)以及最优子结构(前面的总数加上此数与前面的数量得出来的答案必是最优),此题可以用动态规划。那么怎么用?从题目入手,往往答案就是最后一个dp的结果,也就是dp[n][x],那x就是满足012非递减子序列个数的序列,满足012条件是前面至少01,满足01的条件是前面至少全0,也就可以设dp[i][x]是前面1~i满足x(0:全为0。1:满足01。2:满足012)条件有n个子序列,那么答案就是dp[n][2],接下来推递推公式:当a[i]=0,前面有dp[i-1][0]个全0子序列,增加这个0,相当于前面dp[i-1][0]个子序列全带上这个0,最后自己本身成为一个序列。dp[i][0]=2dp[i-1][0]+1;当a[i]=1,一样的,前面有dp[i-1][1]个01子序列,这些序列每个都带这个1又是dp[i-1][1]个,注意,他自己不能单独成一个序列,不能+1!但跟01凑完了可以跟全0凑——只要0的所有序列后面+1又是dp[i-1][0]个。dp[i][1]=2dp[i-1][1]+dp[i-1][0];当a[i]=2,同理,dp[i][2]=2dp[i-1][2]+dp[i-1][1]。这样就能得出结果了,但这样还能做的更好——我们发现递推公式i只和i-1有关,也就是说只要记录上一个数据不断刷新就行了,这样能省大量的空间!
算法难点:只要两层dp就够,那么如何做到算一次存一层,再算一次存另一层?a[i%2]可以解决,但是写起来好麻烦,不如每算一次拷贝一次,拷贝就用move()函数,比如把b[]拷贝到a[]就是b=move(a)(其中a[]和b[]是动态数组),就可以用一个数组操作,操作完后拷贝了,时间也快(用move()是改变其地址,效率o(1))。
研究时间:2021.8.5 2h

【代码因过于简单就略了】

13.明显用动态规划但超时问题
原题:https://leetcode-cn.com/problems/maximum-number-of-points-with-cost/
问题:m∗*∗n的矩阵中每一行选一个数,数的总和是得分,但每选一次都要扣abs(c[i]-c[i-1])分,也就是列数偏移量,求最高最后得分。(m,n<105&&mn<105)
算法类型:优化动态规划(动态规划+前后缀和)
时间复杂度:o(mn)
空间复杂度:o(mn)
算法思路:最优子结构和无后效性,傻子都看得出来用动态规划:dp[i][j]=max(dp[i-1][j’]+abs(j-j’))+a[i][j]。其中max(dp[i-1][j’]+abs(j-j’))就是去上一层遍历一遍找到最大值,我交了一发,t了,回头想一下,这他喵时间是o(mn2)啊!不t就有鬼了。但这题怎么想都只能用动态规划啊,那只能优化动态规划了。众所周知,动态规划至少o(mn),能优话的就是遍历上一层的最大值了:算每个值都要遍历一次,能否只遍历一次上层就把所有最大值找出来呢?从公式入手——如果把max(dp[i-1][j’]+abs(j-j’))里的j提出来,max就只跟j’有关了(跟j无关的好处是在i层dp任何j都可以通用该max),公式就变成dp[i][j]=max(dp[i-1][j’]+j’)+a[i][j]-j(j’<j)和dp[i][j]=max(dp[i-1][j’]-j’)+a[i][j]+j(j’>j),也就是说dp[i][j]=max(上层[0,j]最大-j,上层[j,n]最大+j)+a[i][j]。若要o(1)遍历求最大值,那就遍历一遍求[1,j]左最大存在l[],再反向遍历[j,n]右最大存在r[],l[i]就表示从[1,i]最大数,r[i]同理,那么公式就成了dp[i][j]=max(l[j]-j,r[j]+j)+a[i][j]。o(2mn),AC!
算法难点:思路有了,但还有不足之处——因为可能存在负数,所以每次计算要将l[],r[]重置-inf,这又多出了o(2n)的计算量,优化后其实可以将一组数据直接存在dp中,另一组和dp取max就行了。边算边取,这样又省时间又省空间。
研究时间:2021.8.6 3h

//力扣提交代码
class Solution {public:long long maxPoints(vector<vector<int>>& points) {int m = points.size(),n = points[0].size();//m为行,n为列vector<long long> last(n);for (int i = 0; i < m; ++i) {vector<long long>now(n);long long presummax = LLONG_MIN;//LLONG_MIN是-inffor (int j = 0; j < n; ++j) {presummax = max(presummax, last[j] + j);now[j] = max(now[j], presummax + points[i][j] - j);}long long postsummax=LLONG_MIN;for (int j = n - 1; j >= 0; --j) {postsummax = max(postsummax, last[j] - j);now[j] = max(now[j], postsummax + points[i][j] + j);}last = move(now);//拷贝(会自动清除now内存,需要重新int)}return *max_element(last.begin(), last.end());//max_element()返回数组中最大数的迭代器}
};

14.周期坐标点问题(待处理)
原题:https://ac.nowcoder.com/acm/contest/11257/H
问题:在直角坐标系有n个不同大小的矩形,有一只每次只能跳d距离的兔子,求出兔子位于某点(x0+0.5,y0+0.5)时无论怎么跳、跳多少次都不会跳到矩形内,如果没有结果输出“NO”。(n,d<105,x,y<109)
算法类型:矩形面积并+线段树
时间复杂度:
空间复杂度:
算法思路:“无论怎么跳、跳多少次”可以发现如果给定一个点,这些点都是周期分布在图上的,如果暴力的话要把地图上的所有点判断能否走,这肯定会t,那么求一个点会不会好一点呢?——把所有的矩形通过压缩的方式(如下图)压成一个跳的范围距离,如果将所有矩形覆盖完后还存在某处没填,那么这处的所有周期点都没被覆盖。
算法难点:
研究时间:2021.8.8 5h

#include<bits/stdc++.h>
#define ll long long
using namespace std;const int N=100005;
vector<pair<int,int> > op0[N],op1[N];
int n,D,t[N*4],tg[N*4];
void change(int k,int l,int r,int x,int y,int v){if (x<=l&&r<=y){//如果存的y头和y尾包含[x,y]t[k]+=v; tg[k]+=v; return;}int mid=(l+r)/2;if (x<=mid) change(k*2,l,mid,x,y,v);if (y>mid) change(k*2+1,mid+1,r,x,y,v);t[k]=min(t[k*2],t[k*2+1])+tg[k];
}
int ask(int k,int l,int r){if (l==r) return l;int mid=(l+r)/2;if (tg[k]+t[k*2]==t[k])return ask(k*2,l,mid);return ask(k*2+1,mid+1,r);
}
int main(){scanf("%d%d",&n,&D);int del=(1<<30)/D*D;for (int i=1;i<=n;i++){int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);x1+=del; y1+=del; x2+=del; y2+=del;vector<pair<int,int> > range1,range2;//range1存压缩图x头和尾,range2存压缩图y头和y尾if (x2-x1>=D) range1.push_back(pair<int,int>(0,D-1));else if ((x2-1)%D>=x1%D)  range1.push_back(pair<int,int>(x1%D,(x2-1)%D));else{range1.push_back(pair<int,int>(x1%D,D-1));range1.push_back(pair<int,int>(0,(x2-1)%D));}if (y2-y1>=D) range2.push_back(pair<int,int>(0,D-1));else if ((y2-1)%D>=y1%D)  range2.push_back(pair<int,int>(y1%D,(y2-1)%D));else{range2.push_back(pair<int,int>(y1%D,D-1));range2.push_back(pair<int,int>(0,(y2-1)%D));}for (auto j:range1)for (auto k:range2){op0[j.first].push_back(k);//op0存[x头]y头和y尾op1[j.second+1].push_back(k);//op1存[x尾]y头和y尾}}for (int i=0;i<D;i++){for (auto j:op0[i])change(1,0,D-1,j.first,j.second,1);for (auto j:op1[i])change(1,0,D-1,j.first,j.second,-1);//cout<<t[1]<<endl;if (t[1]==0){puts("YES");printf("%d %d\n",i,ask(1,0,D-1));return 0;}}puts("NO");}

15.按位与与按位或问题
原题:https://ac.nowcoder.com/acm/contest/11259/D
问题:给两数组b[2,n],c[2,n],求一个数组a[1,n],使得a[i]|a[i+1]=b[i+1],a[i]+a[i+1]=c[i+1]。(n<105,ai<230,bi<231)
算法类型:位运算
时间复杂度:o(nlogn)
空间复杂度:o(3n)
算法思路:从题目数据来看,只要a中固定了一个数,其他都可以算出来,但如果通过枚举a[1]来算的话就o(n2),会t,所以不能枚举。我们再看数据范围,230、231明显是位运算,|也是,但是c他是+呀,有没有办法把他变成位运算?很难想——c[i+1]=a[i]+a[i+1]=a[i]|a[i+1]+a[i]&a[i+1]=b[i+1]+a[i]&a[i+1];(记住结论就行)。也就是可以设一个d[],使d[i+1]=c[i+1]-b[i+1]=a[i]&a[i+1];这样就成了一个位运算的题——两个数每一位之间或与与互不干扰,这样就可以通过或与与算出连锁下每个a的其中相同一位的结果,从而算出此位的a[]有多少种,最后再将每个a的所有位的结果相乘就是答案。
算法难点:计算a的一位有多少种简单,但有多少种这样的a[]是头疼的,我们可以从一组b和d开始考虑,如果b=0,d=0,那a[-1]与a[]都是0,如果前面的a[-1]结果是0,那就能继续判断,如果是1,那就无结果,如果b=0,d=1,还要分两种情况,即如果前面的是0后面就1,前1后就0,也就是说是否能合法要看前面的一位,这不经想起了dp——我们不需要把所有a列出来,就设一个bit0,bit1代表上一位是0或1存在多少种合法情况,如下表,如果b=0,d=0,那么现在的nowbit0=bit0,如果b=0,d=1则nowbit0=bit1,nowbit1=bit0,然后状态转移bit0=nowbit0,bit1=nowbit1;最后该位的答案就是bit0+bit1(由于有连锁性,即使有的bd能产生两种情况,也是取决于前面一位,所以每个位数最多情况为2种,每位答案相乘即是所求答案,一定小于230
研究时间:2021.8.11 6h

#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int N = 1e5 + 7;
ll b[N], c[N], d[N];
signed main() {int n;scanf("%d", &n);for(int i = 2; i <= n; ++i) scanf("%lld", b + i);for(int i = 2; i <= n; ++i) scanf("%lld", c + i);for(int i = 2; i <= n; ++i) d[i] = c[i] - b[i];ll ans = 1;for(int i = 0; i <= 31; ++i) {int bit0 = 1, bit1 = 1;//初始默认前面0,1均可取for(int j = 2; j <= n; ++j) {int nowbit0 = 0, nowbit1 = 0;int x = b[j] >> i & 1;int y = d[j] >> i & 1;if(!x && !y) nowbit0 = bit0;//b|d=0,b&d=0else if(x && !y) nowbit1 = bit0, nowbit0 = bit1;//b|d=1,b&d=0else if(x && y)  nowbit1 = bit1;//b|d=1,b&d=1else {bit1=bit0=0; break;}//b|d=0,b&d=1 不可能有结果,终止循环bit1 = nowbit1, bit0 = nowbit0;//确定当前数位状态}ans *= bit1 + bit0;//更新结果if(!ans) break;//若存在非法,直接跳出循环}printf("%lld\n", ans);return 0;
}

input&output:

16.位运算求满足要求的数组个数
原题:https://codeforces.com/contest/1557/problem/C
问题:有一个大小为n的数组a[],每个数都是小于2k的非负数,求满足a[1]&a[2]&a[3]&……&a[n]>=a[1]⊕a[2]⊕a[3]⊕……⊕a[n]的数组个数(1≤n≤2e5 , 0≤k≤2e5)(结果对1e9+7取模)
算法类型:数位dp+排列组合+快速幂
时间复杂度:o(k)
空间复杂度:o(1)
算法思路:根据题目所给数据可知,最大值能达到22e5,再加上是位运算题,我们就不需要知道a[]的具体值,只需要求所有a[]的每一个二进制位组成的01数组有多少组满足题意,由于枚举,每个二进制位满足题意的结果一样,那么所有的结果就是所有位数满足题意的结果相乘,而且每位满足题意的结果需要在o(1)的时间算出来。题目就简化成了:
①对于每一位二进制,用o(1)的时间求n个二进制数组b[]有多少种满足题意。
②对于所有位二进制,第i位的结果对第i-1位结果的影响
对于①通过题目发现,在所有&运算中,除非b[]全为1时等于1,否则等于0,而所有⊕运算等于0和等于1的结果各占一半。要满足题意,设数组种类有x=2n种,根据&的值分情况讨论:
Ⅰ当&为1时,⊕当n为偶数时为0,当n为奇数时为1。均满足条件,但一个是大于一个是等于。
Ⅱ当&为0时,满足条件的只有等于,由于1或0的结果各占一半,当n为偶数时⊕有x/2-1(去除Ⅰ)个0,当n为奇数时⊕有x/2个0。
于是我们可以令n=2代表所有偶数结果,n=3代表所有奇数结果作图(如下图),观察发现当n为偶数,“=”有y1=x/2-1种,“>”有1种,当n为奇数,“=”有y2=x/2+1种,无">“。这就用o(1)解决了一位的问题
对于②,可知如果i位是“=”,那么i-1位还需要”=“或“>“的情况出现,如果i位是“>”,那下面则可无需判断,直接有xi-1的组合。通过下图发现,当n为奇数时没有”>“的情况,也就是说只要把二进制每一位”=“的结果相乘就是答案,即ans=y2k,可o(1)算出。当n为偶数时,情况就复杂了:先考虑最高位i,如果为”>”,ans+=xi-1,如果为"=“,还需通过第i-1位判断:如果为”>“,ans+=y11xi-2,如果为”=“,还需通过第i-2位判断:如果为”>“,ans+=y12xi-3,如果为”=“,还需通过第i-3位判断:……也就是可以直接分析第一个”>“出现在哪一位,如果这个结果符合题意,上面的每层位是”=“,有y1种,下面的怎么取都可以,有x种,由于本位的“=”只有一种,所以第i位ans+=y1i-1xk-i,可o(k)算出。
算法难点:该算法灵活度极高,将数组拆分成k个二进制数组,通过每位用位运算规律计算满足题意结果,并生成奇偶分类讨论,还要使用动态规划考虑到各个位之间的联系,通过排列组合求”>"上下位的情况数,最后通过快速幂运算答案。
研究时间:2021.09.16 18h

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MOD = 1e9 + 7;
ll qkpow(ll a,ll n,ll mod) {ll base=a,res=1;while(n) {if(n&1)res=(base*res)%mod;base=(base*base)%mod;n>>=1;}return res;
}
int main() {ll t;cin>>t;while(t--) {ll n,k,ans=0;cin>>n>>k;if(n&1)ans=qkpow(qkpow(2,n-1,MOD)+1,k,MOD);else {ans=qkpow(qkpow(2,n-1,MOD)-1,k,MOD);for(ll i=k; i>=1; i--)ans=(qkpow(qkpow(2,n-1,MOD)-1,k-i,MOD)*qkpow(qkpow(2,n,MOD),i-1,MOD)%MOD+ans)%MOD;}cout<<ans<<endl;}return 0;
}

17.k倍二进制诈骗问题
原题:https://codeforces.com/contest/1562/problem/C
问题:有个长度为n的01串s,你需要找出长度至少为n/2(向下取整)的不等子串s1和s2,使得s1=k∗*∗s2(k为非负整数)(2<=n<=2e4)
算法类型:new
时间复杂度:o(n)
空间复杂度:o(n)
算法思路:这个题比较阴间,光是看用例就会带偏:我们需要枚举所有符合条件的s1,再找到s2,使得s2左移任意位能变成s1则为答案,但算法复杂度是o(n4),必t,枚举也麻烦,应该想其他解决方案。假设我们找到了s2,s2左移一位变s1,即k=2,也就是s1=s2+0。这样的话如果我从n/2向右找,但凡找到0,不管s1s2是什么,都是答案;同理,k=1时也可以从n/2向左找0,即s1=0+s2,将该问题转化为了找0问题,最后如果没0(即全1串)就随意输出。
算法难点:难点主要在于将k设为特殊值来简化问题。
研究时间:2021.09.04 2h

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main() {ll t,n;cin>>t;while(t--) {string s;cin>>n>>s;int flag=0;for(int i=0; i<n/2+n%2&&!flag; i++)if(s[i]=='0') {cout<<i+1<<" "<<n<<" "<<i+2<<" "<<n<<endl;flag=1;}if(flag)continue;for(int i=n-1; i>=(n-n/2)&&!flag; i--)if(s[i]=='0') {cout<<"1"<<" "<<i+1<<" "<<"1"<<" "<<i<<endl;flag=1;}if(flag)continue;cout<<"1"<<" "<<n-1<<" "<<"2"<<" "<<n<<endl;}return 0;
}

18.整除运算关系dp问题
原题:https://codeforces.com/contest/1561/problem/D1
问题:有n阶台阶,从高到低编号1到n。有两种走法:设当前位置为x:1.走1到x-1步。2.走到x/k(2<=k<=x,x/k向下取整)。求从n走到1要多少步(2<=n<=4e6)
算法类型:动态规划+前缀和+筛法
时间复杂度:o(<n2)
空间复杂度:o(3n)
算法思路:这题很容易看出是dp,第一走法用前缀和不多讲,但第二走法就需要从头除到尾了,但是时间复杂度尾o(n2),t了。怎么办?既然除的会有很多重复,那我将dp[i]+=dp[i/j]中的所有i/j在前面执行的时候全部用数组保存,然后直接除了就可,BUT!

你没有这么多内存来存下标,而且算法复杂度并没有得到优化(依旧是(n2)。也就是说我们只是一个一个加起来都会超时!那我们是否可以像前面一样的用前缀和?使复杂度降至o(n)级别?
我们把第二走法每种结果写出来,所列出来的所有的商x均为+dp[x],可以发现,如果用前缀和的话,前面的值可能会发生变化,但可以发现每层的变化很少,每次变化也是上次+1。所以,每当我们算出一个dp[x]的值后,我们可以预先将x的所有倍数加上这个dp[x](因为y/k=x等价于y=x*k),然后为了避免重复,还要在x+1的所有倍数减去这个dp[x](由于dp是前缀和,减去相当于少了之前的某个结果)。
算法难点:这题代码理解起来相当困难,它打破了常规的静态前缀和,通过筛法删减来实现动态前缀和,最难理解的就是:如果在每步运算中去重。
研究时间:2021.09.07 10h

#include<bits/stdc++.h>
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define ll long long
using namespace std;
const ll MAXN= 4e6+700;
ll n,m,dp1[MAXN],dp2[MAXN],sum[MAXN];
int main() {cin>>n>>m;dp1[1]=dp2[1]=1;for(ll i=1; i<=n; i++) {(dp1[i]+=sum[i]+dp2[i-1])%m;dp2[i]=(dp2[i-1]+dp1[i])%m;(sum[i+1]+=sum[i])%m;for(ll j=2; j*i<=n; ++j)  {(sum[j*i]+=dp1[i])%=m;(sum[min(j*(i+1),n+1)]+=m-dp1[i])%=m;}}cout<<dp1[n]%m<<endl;return 0;
}

19.错位相加问题
原题:https://codeforces.com/contest/1567/problem/C
问题:有个人在算加法的时候进位时会向更高的一位进1,现在只知道他算错的结果,求有多少种原式会让他算成这个结果
算法类型:动态规划+new
时间复杂度:o(lnn)
空间复杂度:o(lnn)
算法思路:这题我一开始还在考虑每个答案有多少种加法合法,其实这题如果想到这点可以秒做:第一位进位进到第三位,第三位进位进到第五位,第五位进位进到第七位……也就是说可以把奇数位数分出来,偶数位数分出来,他们之间互不干扰,可以之间从答案中错位判断原加数是什么。
算法难点:null
研究时间:2021.09.25 3h

#include <bits/stdc++.h>
using namespace std;
int main() {int T, n;cin >> T;while (T--) {string n;cin >> n;int s1 = 0, s2 = 0;for (int i = 0; i < n.size(); i += 2) s1 = s1 * 10 + n[i] - '0';for (int i = 1; i < n.size(); i += 2)s2 = s2 * 10 + n[i] - '0';if (!s2) cout << s1 - 1 << endl;else cout << 1LL * (s1 + 1) * (s2 + 1) - 2 << endl;}return 0;
}

20.矩阵中选择求和问题
原题:https://leetcode-cn.com/problems/minimize-the-difference-between-target-and-chosen-elements/
问题:给一个m∗*∗n的矩阵,每行选一个数,求其之和与目标值相差最小绝对值(1<=n,m,a[][]<=70)(1<=target<=800)
算法类型:动态规划
时间复杂度:o(m2n)
空间复杂度:o(mn)
算法思路:由于每种组合都有一个答案,组合共有mn的情况,一一列举肯定会t,我一开始想到了优化枚举——将每行排序,然后组合,当枚举这和比目标值大的时候,再往后枚举差值会越来越远,所以之间跳过此行枚举,然而他的结果依然近似于o(mn)。再看看数据,会发现它的a[][]值开的额外小,如果全部加起来最多也是4900,那我们无需知道其组合方式,只需知道求和结果就行,把所有可能结果存起来,用bool b[]存,b[i]代表求和为i是否存在,那么对于每层的数值,加上前面能产生所有的结果(从b[1]遍历到b[4900])就是新的结果,最后o(4900)遍历最小绝对值即可。
算法难点:难点在于从枚举问题转化为背包类型动态规划,大大降低了时间复杂度。
研究时间:2021.10.7 3h

class Solution {public:int minimizeTheDifference(vector<vector<int>>& mat, int target) {int m = mat.size(), n = mat[0].size();int maxsum = 0;// 什么都不选时和为 0vector<int> f = {1};for (int i = 0; i < m; ++i) {int best = *max_element(mat[i].begin(), mat[i].end());vector<int> g(maxsum + best + 1);for (int x: mat[i]) {for (int j = x; j <= maxsum + x; ++j) {g[j] |= f[j - x];}}f = move(g);maxsum += best;}int ans = INT_MAX;for (int i = 0; i <= maxsum; ++i) {if (f[i] && abs(i - target) < ans) {ans = abs(i - target);}}return ans;}
};

仲舟原创,未经允许禁止转载!

仲舟の奇妙算法(二)【个人版】相关推荐

  1. 仲舟の奇妙算法(一)【个人版】

    1.求01图中全是1的最大子矩阵 问题:n*m的01图,输出全为1的最大子矩阵中1的个数 (n,m<103) 算法类型:动态规划 时间复杂度:o(nm) 空间复杂度:o(nm) 算法思路:将输入 ...

  2. 使用pytorch从零开始实现YOLO-V3目标检测算法 (二)

    原文:https://blog.csdn.net/u011520516/article/details/80212960 博客翻译 这是从零开始实现YOLO v3检测器的教程的第2部分.在上一节中,我 ...

  3. 从零开始学数据结构和算法(二)线性表的链式存储结构

    链表 链式存储结构 定义 线性表的链式存储结构的特点是用一组任意的存储单元的存储线性表的数据元素,这组存储单元是可以连续的,也可以是不连续的. 种类 结构图 单链表 应用:MessageQueue 插 ...

  4. Unicode双向算法详解(bidi算法)(二)

    作者:黄邦勇帅(原名:黄勇)2019-10-17 Unicode双向算法详解(bidi算法)(二) 本文为原创文章,转载请注明出处,或注明转载自"黄邦勇帅(原名:黄勇) 本文是对<C+ ...

  5. 票据ticket实现方式java代码_Java代码实践12306售票算法(二)

    周五闲来无事,基于上一篇关于浅析12306售票算法(java版)理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1初始化一列车厢的票据信息 /** * 生成 ...

  6. matlab算法(二维傅立叶级数变换)

    说明 Y = fft2(X) 使用快速傅里叶变换算法返回矩阵的二维傅里叶变换,这等同于计算 fft(fft(X).').'.如果 X 是一个多维数组,fft2 将采用高于 2 的每个维度的二维变换.输 ...

  7. 数据结构和算法之五:排序算法二

    数据结构基础之排序算法二 学习算法,排序算法当然是不能少的,这次我们来学习一下基础的选择排序,冒泡排序,以及大名鼎鼎的快速排序. 选择排序 选择排序,非常好理解,就是找最小的数放到第一位,然后从第二个 ...

  8. 售票java代码_Java代码实践12306售票算法(二)

    周五闲来无事,基于上一篇关于浅析12306售票算法(java版)理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1初始化一列车厢的票据信息 /** * 生成 ...

  9. java按顺序售票方法_java_Java代码实践12306售票算法(二),周五闲来无事,基于上一篇关 - phpStudy...

    Java代码实践12306售票算法(二) 周五闲来无事,基于上一篇关于浅析12306售票算法(java版)理论,进行了java编码实践供各位读者参考(以下为相关代码的简单描述) 1.订票工具类 1.1 ...

最新文章

  1. EndNote 高校_【工具】EndNote使用小记
  2. Ubuntu 安装任意版本Django
  3. UA MATH564 概率论V 中心极限定理
  4. 【学习笔记】Sass入门指南
  5. tensorflow tf.train.Saver.restore() (用于下次训练时恢复模型)
  6. nio java 内核拷贝_大文件拷贝,试试NIO的内存映射
  7. 阿里云ECS服务器磁盘空间异常,或者爆满
  8. JavaScript计算两个日期相差天数/分钟/小时
  9. el-select下拉框组件el-option如何使用v-for动态渲染问题 - 方法篇
  10. java为什么不能输入钢筋符号_input.nextDouble();找不到符号
  11. 动态规划——删除并获得点数(Leetcode 740)
  12. 用html编写勾股定理,一种勾股定理演示器的制作方法
  13. 目标检测——夏侯南溪目标检测模型之输出信息显示
  14. office文档 在线预览 (doc、ppt、xls)
  15. 我用一篇文章,让你快速上手Kotlin
  16. 使用暴风激活激活系统,浏览器被劫持问题解决
  17. 02 - Redis源码结构介绍
  18. 计算机英特尔显卡在哪找,Win10英特尔显卡设置图标不见了该怎么办 - 系统之家...
  19. 提供一套基于SpringBoot-shiro-vue的权限管理思路.
  20. hdu5064 Find Sequence 单调性dp

热门文章

  1. Three.js教程:三维坐标系
  2. 备抵附加账户的期末余额_备抵附加账户的期末余额不是固定的,当其余额在借方时...
  3. python中setup函数_Python包管理工具setuptools之setup函数参数详解
  4. 论文中的常用缩略语与关键词
  5. 大学计算机英语句子,英语经典句子(精选100句)
  6. 机器学习系列文章——算法的实现(knn,朴素贝叶斯,决策树,模型评估)
  7. 网页暂停一段时间后声音延迟出现解决办法
  8. 《网安学习之道》第一季计算机基础18_生成树协议STP
  9. CSS 实现文本中间省略号
  10. 通过编程模拟一个简单的饮料自动贩卖机_你喝过自动贩卖的现榨橙汁吗?