1.求01图中全是1的最大子矩阵
问题:n*m的01图,输出全为1的最大子矩阵中1的个数 (n,m<103)
算法类型:动态规划
时间复杂度:o(nm)
空间复杂度:o(nm)
算法思路:将输入的矩阵a[][]转化为当前位置列以上的连续1个数h[][],再通过left[]和right[]来记录以此位置的h[][]为矩阵高,左右最长距离(所以左右的数必须大于等于h[][]才能构成矩阵),然后通过行1数乘列1数即(right[]-left[]-1)*h[][]来算包含当前位置的全1最大矩阵。
算法难点:计算每一行的left[]和right[],通过h[]从左往右比较,for(; now && h[i][j] <= h[i][now]; now=left[now]);用于找到比h[][]相等和更大的最左值, now=left[now]用并查集的方法快速访问全相等时最左端的值,right[]同理。
研究时间:2021.7.20 3h

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stack>
using namespace std;
const int N = 500 + 5;int n,m;
int a[N][N];
int h[N][N];
int left[N],right[N];int main() {while(scanf("%d%d",&n,&m) == 2) {memset(h,0,sizeof h);for(int i=1; i<=n; i++) for(int j=1; j<=m; j++) scanf("%d",&a[i][j]);for(int i=1; i<=n; i++) {for(int j=1; j<=m; j++) {if(a[i][j] == 1) h[i][j] = h[i-1][j] + 1;else h[i][j] = 0;}}/* for(int i=1; i<=n; i++)for(int j=1; j<=m; j++){printf("%d",h[i][j]);putchar(j==m?'\n':' ');}*/int ans = 0;for(int i=1; i<=n; i++) {for(int j=1; j<=m; j++) {int now = j - 1;for(; now && h[i][j] <= h[i][now]; now=left[now]);//!!!left[j] = now;}for(int j=m; j>=1; j--) {int now = j + 1;for(; now != m+1 && h[i][j] <= h[i][now]; now=right[now]);//!!!right[j] = now;}for(int j=1; j<=m; j++){ans = max(ans, (right[j]-left[j]-1)*h[i][j]);//printf("right[%d]=%d,left[%d]=%d,(%d-%d-1)*h[%d][%d]=%d\n",j,right[j],j,left[j],right[j],left[j],i,j, (right[j]-left[j]-1)*h[i][j]);}}printf("%d\n",ans);}return 0;
}

input:

debug:
h[][]

left[],right[],(right[]-left[]-1)*h[][]

output:

2.多次操作单次输出问题
原题:https://ac.nowcoder.com/acm/contest/11253/K
问题:规则是有个队列,每次放入一个数i,比i大的数都要被消除,输入数字总数n,有k个要求,每个要求是放入第p个数中队列还有x个数,求1~n的放入顺序,如果无法实现就输出-1。(n,k<106)
算法类型:new
时间复杂度:o(3n+k)
空间复杂度:o(4n)
算法思路:∵(n,k<10^6),所以不能根据要求一遍遍操作,而且要按照要求顺序操作,每个要求操作以后部分范围值必须出结果,否则必T,通过要求可知,若放入第p个数中队列还有1个数,这个数将比前面的都小,如果剩1数中最大的p,那么这个位置一定是1,换句话说,应该从后向前顺序放置数字,如果剩x个就在前面随机x-1个位置放x-1个顺序数,存入第i个数前有b[i]个数,m[i]为p-x即已删除的个数作为判断-1的条件,若删除的个数突然减少就-1,用一个栈来存还没放进去的数字,并且始终保持栈是顺序出来,a[]用来存结果。
算法难点:从后往前找有要求的a[],若存在x个数,再从a[]到a[-x+1]存在的数依次放回栈中,栈依然顺序,再从a[-x+1]到a[]依次放入,此时a[-x+1]到a[]顺序,符合题目要求。
研究时间 2021.7.19 3h

#include<bits/stdc++.h>
#define N 1000010
using namespace std;
int n,k,p,x,a[N],b[N],m[N],s=0;
stack<int >q;
int main(){cin>>n>>k;memset(b,0,sizeof b);memset(a,0,sizeof a);memset(m,0,sizeof m);for(int i=n;i>=1;i--)q.push(i);while(k--){cin>>p>>x;if(x>p){cout<<-1<<endl;return 0;}b[p]=x;m[p]=p-x;if(m[p]>s)s=m[p];}/*cout<<"i  :";for(int i=1;i<=n;i++)cout<<i<<" ";cout<<endl<<"b[]:";for(int i=1;i<=n;i++)cout<<b[i]<<" ";cout<<endl<<"m[]:";for(int i=1;i<=n;i++)cout<<m[i]<<" ";cout<<endl;*/for(int i=n;i>=1;i--){if(b[i]!=0){for(int j=i;j>=i-b[i]+1;j--){if(a[j]!=0)q.push(a[j]);}for(int j=i-b[i]+1;j<=i;j++){a[j]=q.top();q.pop();}if(m[i]>s){cout<<-1<<endl;return 0;}else s=m[i];}/*printf("i=%d,a[]:\n",i);for(int i=1;i<=n;i++)cout<<a[i]<<" ";cout<<endl;*/}for(int i=1;i<=n;i++){if(a[i]==0){a[i]=q.top();q.pop();}i==1?cout<<a[i]:cout<<" "<<a[i];}
}

input:

debug:

output:

3.多维多种情况次数计算问题
原题:https://ac.nowcoder.com/acm/contest/11213/D
问题:有a,b,c三堆钞票,每次只能拿走一堆的300/450/750元,直到不能拿为止,有几种拿法(一开始就不能拿也算一种)(结果对1000000007求余)(a,b,c<104)
算法类型:动态规划
时间复杂度:o(<n2)
空间复杂度:o((n/50)3)
算法思路:这是一个多情况问题,对于每种情况,之后的情况又有多种情况,这种就需要用到递归的dp了,但由于是三维数组,104*3必爆栈,还好给的数据可以除以50以减小内存,开一个2003的dp[][][],记录当钞票为a b c时的结果,那么这个结果等于3*3结果之和即dp[][][]=dp[-300/50][][]+dp[][-300/50][]+dp[][][-300/50]+dp[-450/50][][]+……
算法难点:dp题目如果询问t次的就要所有dp,如果只询问一次就选择dp。dp算法好写,就是理解起来困难点,要注意递归中如果dp有数据就直接用数据,没有就要经过计算,还要注意如果买不起的时候要置1而不是0,因为dp是下一级dp之和,最底层的基至少得有1。
研究时间:2021.7.21 2h

#include <bits/stdc++.h>
using namespace std;
const int p = 1000000007;
int a, b, c;
int dp[203][203][203];
int DP(int x, int y, int z) {if (dp[x][y][z] != -1) return dp[x][y][z];int ret = 0;if (x >= 6) (ret += DP(x - 6, y, z)) %= p;if (x >= 9) (ret += DP(x - 9, y, z)) %= p;if (x >= 15) (ret += DP(x - 15, y, z)) %= p;if (y >= 6) (ret += DP(x, y - 6, z)) %= p;if (y >= 9) (ret += DP(x, y - 9, z)) %= p;if (y >= 15) (ret += DP(x, y - 15, z)) %= p;if (z >= 6) (ret += DP(x, y, z - 6)) %= p;if (z >= 9) (ret += DP(x, y, z - 9)) %= p;if (z >= 15) (ret += DP(x, y, z - 15)) %= p;if (ret == 0) ret = 1;//printf("dp[%d][%d][%d]=%d\n",x,y,z,ret);return dp[x][y][z] = ret;
}
int main() {cin >> a >> b >> c;a /= 50;b /= 50;c /= 50;memset(dp, -1, sizeof dp);//printf("a/50=%d b/50=%d c/50=%d\n",a,b,c);cout << DP(a, b, c) << endl;return 0;
}

input:

debug:

output:

4.博弈问题
原题:https://ac.nowcoder.com/acm/contest/11166/A
问题:有两堆石头,ab两人拿,每人可以从一堆拿x个,在另一堆拿kx(k>=0)个,谁刚好拿完谁胜,a先拿,双方最右策略,输入两堆数n,m(n,m<5*103),输出获胜者
算法类型:筛法
时间复杂度:o(>nm)
空间复杂度:o(nm)
算法思路:类似这种最优策略问题,往往是给出初始条件就知道谁赢的“还没开始就已经结束”的游戏,进而可以将题目转化为操作次数奇a赢偶b赢,再深入就是可以从a的第一次拿开始分析——如果a一次性全拿完,则a赢;如果a一次性拿不完,就必须控制剩下偶数次数让自己获胜,如果怎么样都无法控制只能是b赢。第一个好理解,第二个类似数学归纳法(算法就是dp)——假设n=2,m=3确定了b会赢,那么我拿一次剩n=2,m=3时a会赢,拓展一下就是a拿一次后到达b赢的结果,那就是a赢。
算法难点:如何判断所给的n喝m能否操作一次到达以往计算过的b赢时的n’和m’,就成了主要的难题,而且如果从后往前判断,要把前面的全部的b访问一次并计算,就成o(n3)了,必T,那我们不如反向思考,如果生成一个b赢的情况n,m,将之后一步所有情况(n+x,m+kx(x,k在不越界的情况下从1到5000)全部算为a,再往后找b,如果不是a则就是b,这样就节省了不少时间,还大大简便了运算,这个方法就叫——筛法。
研究时间:2021.7.25 3h

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
bool F[MAXN][MAXN]= {0};int main() {int cnt = 0;for(int i=0; i<=5000; i++)for(int j=0; j<=5000; j++)if (!F[i][j]) {for (int k = 1; i + k <= 5000; k++)for (int l = 0; j + k * l <= 5000; l++)F[i + k][j + k * l] = 1;for (int k = 1; j + k <= 5000; k++)//由于m与n无顺序,需要反向再算一遍for (int l = 0; i + k * l <= 5000; l++)F[i + k * l][j + k] = 1;}int T;cin >> T;while (T--) {int n,m;cin >> n >> m;puts(F[n][m] ? "Alice" : "Bob");}return 0;
}

input&output:

5.无向完全连通图求三角形个数问题
原题:https://ac.nowcoder.com/acm/contest/11254/J
问题:有n个结点构成的无向完全连通图,所有边通过生成种子已经确定是白边或者黑边,求这个图中用相同颜色边构成的三角形的总数。(n<=8000)
算法类型:逆向思维
时间复杂度:o(n2)
空间复杂度:o(n2)
算法思路:如果要算同色边三角形的个数,首先可以想到随机列举三个点,再判断是否能构成同色,那这样就是n3,必t,如果n<=8000的话,只能每两个点(即一条边)全部遍历,达到n2或者n2logn的算法,那如果只能算两个点的话,是不能判断能构成三角形的,这题如果去计算多少个三角形,怎么想都是n3,那能不能逆向思维,判断有多少个一定构不成三角形的,将总数减去构不成的三角形就是所求的三角形呢?答案是肯定的。以一个点为顶点,如果他的两条边是同色,它可能构成三角形,如果是异色,它必构不成同色三角形,那我们可以通过计算一个点的所有白线和黑线总数a,b,那这个点产生的异色对有ab个,答案就是n(n-1)(n-2)-sum(aibi)/2。(因为每条边会重复算,所以要除2)
算法难点:就是难想,写倒是挺好写
研究时间:2021.7.25 3h

#include<bits/stdc++.h>
using namespace std;namespace GenHelper//生成种子
{unsigned z1,z2,z3,z4,b,u;unsigned get(){b=((z1<<6)^z1)>>13;z1=((z1&4294967294U)<<18)^b;b=((z2<<2)^z2)>>27;z2=((z2&4294967288U)<<2)^b;b=((z3<<13)^z3)>>21;z3=((z3&4294967280U)<<7)^b;b=((z4<<3)^z4)>>12;z4=((z4&4294967168U)<<13)^b;return (z1^z2^z3^z4);}bool read() {while (!u) u = get();bool res = u & 1;u >>= 1; return res;}void srand(int x){z1=x;z2=(~x)^0x233333333U;z3=x^0x1234598766U;z4=(~x)+51;u = 0;}
}
using namespace GenHelper;
int s0[8005],s1[8005];
int n,seed;
int main(){scanf("%d%d",&n,&seed);srand(seed);for (int i=1;i<=n;i++)for (int j=i+1;j<=n;j++)if (read()) ++s1[i],++s1[j];else ++s0[i],++s0[j];long long ans=0;for (int i=1;i<=n;i++)ans+=1ll*s1[i]*s0[i];ans=1ll*n*(n-1)*(n-2)/6-(ans/2);cout<<ans<<endl;
}

input&output

6.双条件统排问题
原题:https://codeforces.com/contest/1551/problem/C
问题:t用例,每个用例有n个字符串,每个字符串只有abcde五种字母,找出用最多条字符串拼成的长字符串,使得长字符串出现最多字母的次数大于其他字母出现的总次数。(t<5000,n<2*105)
算法类型:统排+贪心
时间复杂度:o(tn)
空间复杂度:o(tn)
算法思路:如果只记录个数不记录字符串顺序,那就先把问题转化为统排问题,随后可以通过以某个字母排序来从最多开始加起,再判断是否满足条件,满足就更新答案,存在这种问题:如下图用例5,对d排序后是d,d,cbdca,a,e,在加到第三个时,不满足条件,之后都不满足,输出为2,但如果我选d,d,a,又满足条件,答案是3。像这样,我们又不能把所有组合的情况数出来(否则必t),那到底哪出错了呢?如果是这种数据,必须是只能o(n)才能保证不超时,求最大值用贪心也没有错,错就错在统排上:我们希望加x字母多而非x字母少的字符串,而排序不可能按两个要求同时排序,如果要,就得给出一个新的比较条件,将两个条件结合成一个新的条件再比较,这成了突破口。x多且非x少,可以转化为x占比率,通过占比率排序,从多往少的加一定没错。
算法难点:我们在统排的时候,如果记录x个数时,可以让其+1,其余的-1,如果所有的都种方法,可以简化成全部数先减strlen(),然后出现一个字母+2,这样cbdca的d值为-3,a的d值却成了-1,a的d占比率高,排在了前面,用贪心再算值,就不会出错了。
(本题含有重载符号,对其不了解的同学可以去了解一下)
研究时间:2021.7.27 3h

#include<bits/stdc++.h>
#define N 200005
using namespace std;int T,n,m,Ts;char c[N];
struct node{int a[5];bool operator<(const node&b)const{//重载(返回类型 operator 重载符号(const 结构体类型 & 变量名)return a[Ts]>b.a[Ts];}
}s[N];
int main(){cin>>T;while(T--){cin>>n;for(int i=1;i<=n;++i){scanf("%s",c+1);m=strlen(c+1);for(int j=0;j<5;++j)s[i].a[j]=-m;for(int j=1;j<=m;++j)s[i].a[c[j]-'a']+=2;}int ans=0;for(Ts=0;Ts<5;++Ts){int sum=0;sort(s+1,s+n+1);for(int i=1;i<=n;++i){sum+=s[i].a[Ts];if(sum<=0)break;ans=max(ans,i);}}cout<<ans<<"\n";}return 0;
}

input&output:

7.多次查询与维护问题
原题:https://ac.nowcoder.com/acm/contest/11255/I
问题:有一个1~n不重复数组a[],他们每个数可以不变或者+1,输出操作完后的最小逆序数对总数。(n<2*105)
算法类型:①二分+归并排序 ②线段树/树状数组
时间复杂度:o(nlogn)
空间复杂度:o(3n)
算法思路:通过多次举例可以发现,如果一个数x+1,最多只能消除前面比它大1的逆序数,如果前面没有这个数,+1没有任何作用,如果后面还有比它小1的数y,x+1之之后还会使y的前面多个逆序数,所以就不加,就能求出修改后的a[],最后就是求a[]的逆序数对了。
算法难点:至于怎么求逆序数对,有两种方法:①归并排序求逆序数对:该方法是先修改后求逆序数。输入时同时记录数值和下标,对数值进行快排后顺序操作,随后按下标快排用归并排序计算逆序对,原理是每个归并如[a,b,c][d,e,f]如果a比d大,由于归并两子序列都是顺序排列,所以b和c也比a大,d的逆序数就是3,用ans += mid - i + 1;就可计算出。②树状数组求逆序数对:该方法是先求原逆序数后修改。也是输入时同时记录数值和下标,假设有一个全为0的数组b[],从前往后算原a[]每个的逆序数,算x之前的逆序数就是b[1]到b[n]的和减b[1]到b[x]的和,ans+=sum(n)-sum(x),再将b[x]=1,将其全转为树状数组的形式(即下面的t[])存储就完工了。最后通过下标判断x+1是否在x前面,如果是就ans–,并且立flag=1,使x-1不再变化。
(注:特别注意ans范围,如果n=2x105,ansmax=n(n-1)/2=1010,得开longlong!!!(我的就是被longlongwa了qwq))
研究时间:2021.7.27 2h

①归并排序求逆序数对(自己AC的)

#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= 2e6+700;
struct node {int data;int pos;
} a[MAXN],b[MAXN];
ll n,ans=0;bool cmp(node a,node b) {return a.data<b.data;
}bool dmp(node a,node b) {return a.pos<b.pos;
}void merge(int low, int mid, int high) {int i, j, k;for (i = low, j = mid + 1, k = i; i <= mid && j <= high; k++) {if (b[i].data <= b[j].data)a[k].data = b[i++].data;else {a[k].data = b[j++].data;ans += mid - i + 1;}}while (i <= mid) a[k++].data = b[i++].data;while (j <= high) a[k++].data = b[j++].data;for (i = low; i <= high; i++) b[i].data = a[i].data;
}void mergesort(int low, int high) {if (low < high) {int mid;mid = (low + high) / 2;mergesort(low, mid);mergesort(mid + 1, high);merge(low, mid, high);}
}int main() {js;cin>>n;for(int i=1; i<=n; i++) {cin>>a[i].data;a[i].pos=i;}sort(a+1,a+1+n,cmp);for(int i=1; i<=n-1; i++)if(a[i].pos>a[i+1].pos) {a[i].data++;swap(a[i],a[i+1]);i++;}sort(a+1,a+1+n,dmp);for(int i=1;i<=n;i++)b[i]=a[i];mergesort(1, n);cout<<ans<<endl;return 0;
}

②树状数组求逆序数对(推荐)

#include<bits/stdc++.h>
#define ll long long
using namespace std;long long ans;
const int N=200005;
int n,p[N],a[N],t[N];void change(int k) {// printf("  into change(%d)\n",k);for (; k<=n; k+=k&(-k)) {t[k]++;// printf("    t[%d]++,t[%d]=%d\n",k,k,t[k]);}
}
int ask(int x) {//printf("  into ask(%d)\n",x);int s=0;for (; x; x-=x&(-x)) {s+=t[x];// printf("    s+=t[%d],t[%d]=%d,s=%d\n",x,x,t[x],s);}return s;
}
int main() {scanf("%d",&n);for (int i=1; i<=n; i++)scanf("%d",&a[i]),p[a[i]]=i;/* cout<<"a[]"<<" ";for(int i=1; i<=n; i++)cout<<a[i]<<" ";cout<<endl<<"p[]"<<" ";for(int i=1; i<=n; i++)cout<<p[i]<<" ";cout<<endl;*/for (int i=1; i<=n; i++) {ans+=ask(n)-ask(a[i]);//printf("i=%d,ans+=ask(%d)-ask(%d),ans=%d\n",i,n,a[i],ans);change(a[i]);}int fl=0;for (int i=2; i<=n; i++)if (p[i]>p[i-1]) fl=0;else if (fl==1) fl=0;else {ans--;fl=1;}cout<<ans<<endl;
}

input:

②debug:
树状数组的查询与维护

output:

8.恐怖的高等数学
原题:https://ac.nowcoder.com/acm/contest/11254/E
问题:t个用例,每个用例输入n,求1~n所有满足x2+y2=k(xy+1) (k>=1)的(x,y)个数。(t<105,n<1018)
算法类型:数论
时间复杂度:o(tlogn+³√n)
空间复杂度:o(³√n)
算法思路:先看数据,遇到1018,必须是o(logn)或o(1)(即给个数直接通过公式算出结果)的算法才不会超时,而这题是求前面之和,和前面有关系往往不是用公式算的,很有可能涉及到降n的次再运算。再看题干,是二元二次方程,遇到二次方往往用韦达定理,由于是二元,我们先固定x,y用韦达定理是y+y’=kx,yy’=x2-1,也就是说如果我(x,y)满足该式子,那么(x,y’)也满足,不妨设x<=y,则由上述式子得y’>=0且y‘<=y,当我将y’替换成y时,又多一组解,如此循环,最后发现确定了一个x的值y可以一直递降到0,当y’=0时,y=kx,则x2+k2x2=k(kx2+1),k=x2(k已经确定),此时y=x3则在递降到最后一层时,总有(x,x3)满足条件,之后就可以逆推,令y=x3,由韦达得(kx-y,x)也成立(注:在这kx-y比x小)然后再令x’=kx-y,y’=x,又有(x’,y’)成立,可遍历到所有结果。而且对于x3的乘法,可以将复杂度o(1018)降为o(106)。大大提高运行效率。又因为(a1,a2)(a2,a3)……所有对的第一个都是由前面一个对的第二个,所以有多少对就相当于有多少个,可以将每个答案存入数组,通过upper_bound返回下标值,该下标值就是前面满足条件个数的和,即题目答案。
算法难点:类似上述递归,可以用到类似辗转相除法的算法计算(即a=x,b=y,存答案后c处理a和b,然后让a=b,b=c,完成两处理量同时右移一步的操作。
(注:这里特别注意数据是否会溢出,1LL因为循环的x是int,相乘需要在前面转换成longlong,__int128是怕longlong相乘会爆,无法比较是否越界,所以还得开int128!!!)
研究时间:2021.7.28 4h

#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 INF = 1e18+7;
vector<ll>ans{1};int main() {js;for (int x = 2; x <= 1000000; x++) {// cout<<"x="<<x<<endl;ll a = x,b = 1LL*x*x*x;//注意数据大小while (1) {//cout<<b<<endl;ans.push_back(b);if (__int128(b) * x * x - a > INF)//注意数据大小break;ll c = b * x * x - a;a = b;b = c;}}sort(ans.begin(), ans.end());/*for(ll i=0;i<ans.size();i++)cout<<ans[i]<<endl;*/int t;cin >> t;while (t--) {ll n;cin >> n;cout <<upper_bound(ans.begin(), ans.end(), n) - ans.begin()<< endl;}return 0;
}

input&output:

debug:
当x=2时所生成的结果

9.多维度模拟问题
原题:https://ac.nowcoder.com/acm/contest/11254/F
问题:有n张卡,卡的数字1~13,要求n张卡通过+−-−∗*∗/运算后等于m并且这n张卡所有等于m的运算结果必须存在过分数(1/1,2/2都不属于分数,2/3才属于),输出所有结果总和以及每一种有效解的卡的组合。(1<=n<=4,m<109)
算法类型:模拟
时间复杂度:o(约132n)
空间复杂度:o(106)
算法思路:既然要求组合,再看这题数据就十位数,这题直接模拟(暴力)没有问题!都暴力了思路也很简单:四个数开四个for,从四个1for到四个13,然后再开两个for,随机选两个进行一个for的四种运算,然后再开一个for和下一个数四种运算,剩下一个再开……思路没有问题,但这么多for怎么写呢?而且随n的不同,开的for也不同,对于这种多维度的问题,最好用递归来写,来康康我们北大同学AC的代码(不愧是北大,我就代码看了三个多小时qwq)……
算法难点:本题难点较多,请仔细阅读代码中的注释!
研究时间:2021.7.28 4h

#include<bits/stdc++.h>
using namespace std;int n,m,ansn;
vector<double> nw;
vector<int> ans[1000010],nww;int solve(vector<double> nw)
{if (nw.size()==1)//终止递归条件:nw只有一个数{if (fabs(nw[0]-m)<1e-9) return 2;//因为有除号,算出来的结果可能会有误差,如果组合的结果和答案偏差1e-9的话,就认为这是没有分数运算的无效答案,返回2(error)。return 0;//构不成m,但以后可能构成,先待定(undetermined)}int sz=nw.size(),ans=0;for (int i=0; i<sz; i++)//在所有数中抽取第i个数准备处理for (int j=i+1; j<sz; j++)//在所有数中抽取第j个数准备处理{vector<double> Nw; Nw.clear();//新开一个给下一个递归的数组for (int k=0; k<sz; k++) if (k!=i&&k!=j) Nw.push_back(nw[k]);//把除i和j剩下的放进去Nw.push_back(nw[i]+nw[j]);//再把i和j相加处理ans=max(ans,solve(Nw));//处理后放下一个递归if (ans==2) return 2;//出现解立即返回2(error),终止递归,排除该答案Nw.pop_back();//把刚刚处理的拿走Nw.push_back(nw[i]*nw[j]);//再把i和j相乘处理ans=max(ans,solve(Nw));//下面同理if (ans==2) return 2;Nw.pop_back();Nw.push_back(nw[i]-nw[j]);ans=max(ans,solve(Nw));if (ans==2) return 2;Nw.pop_back();Nw.push_back(nw[j]-nw[i]);//由于减有先后性,需要反减一遍ans=max(ans,solve(Nw));if (ans==2) return 2;Nw.pop_back();if (fabs(nw[j])>1e-9)//由于除法可能会存在除0的非法操作,所以这里要判断{Nw.push_back(nw[i]/nw[j]);int nww=2;if (nw[i]/nw[j]-floor(nw[i]/nw[j]+1e-8)>1e-9) nww=1;//floor是向下取整函数,ceil是向上取整函数,如果nw[i]除nw[j]的不是分数,那么这次结果是2。ans=max(ans,min(nww,solve(Nw)));//如果nww为2,再给之后运算机会,如果还有分数,min(2,1);如果nww为1,已经做了一次除法,之后没分数也不要紧,min(1,2)。if (ans==2) return 2;Nw.pop_back();}if (fabs(nw[i])>1e-9){Nw.push_back(nw[j]/nw[i]);int nww=2;if (nw[j]/nw[i]-floor(nw[j]/nw[i]+1e-8)>1e-9) nww=1;ans=max(ans,min(nww,solve(Nw)));if (ans==2) return 2;}}return ans;
}int main()
{scanf("%d%d",&n,&m);for (int i=1; i<=13; i++)//第一个数for (int j=i; j<=(n>=2?13:i); j++)//第二个数for (int k=j; k<=(n>=3?13:j); k++)//第三个数for (int l=k; l<=(n==4?13:k); l++)//第四个数{nw.clear(),nww.clear();//nww存结果,nw拷贝用来运算nw.push_back(i),nww.push_back(i);if (n>1) nw.push_back(j),nww.push_back(j);if (n>2) nw.push_back(k),nww.push_back(k);if (n>3) nw.push_back(l),nww.push_back(l);if (solve(nw)==1) ans[++ansn]=nww;//我每种情况只能有一种结果,所以用1来表示用了除法是有效解(true),0用来表示当前运算还没有找到有效解(以后可能会有)(undetermined),2用来表示有方法没有用用除法得到的无效解(error),立即终止继续递归,减少运算量}printf("%d\n",ansn);for (int i=1; i<=ansn; i++)for (int j=0,sz=ans[i].size(); j<sz; j++) printf("%d%c",ans[i][j],j==sz-1?'\n':' ');//ans[]=a,b,c,dreturn 0;
}

input&output:
(虽然不知道结果怎么来到但我相信没毛病→v→)

10.含变量排序问题(待解决)
原题:https://ac.nowcoder.com/acm/contest/11256/J
问题:有n个珠宝在海中(x,y,z)位置(z轴正方向竖直向下),以每秒v的速度下沉,你从(0,0,0)的位置,每秒可以花费d2(d为你与某珠宝的距离)力量捞起某宝石,求把全部宝石捞完的最小力量。(n<300,x,y,z<1000)
算法类型:new
时间复杂度:o(约n3)
空间复杂度:o(3n)
算法思路:这题很容易被误认为是用贪心算法:拿v排序的在最底层,拿下一步最大距离变化量排序的在第二层,通过算式求导求距离最大变化率的在第三层。这些的贪心都只能做到局部最优,并不是全局最优(比如第一层v小但z大d变化就大,第二、三层拿走当前变化(率)最大的没有拿走下一步变化(率)最大的)。那怎么办呢?我们先从问题出发,再去选用合适的方法:求最小的力量,那么ans=x12+y12+(z1+v1t1)2+x22+y22+(z2+v2t2)2+……+xn2+yn2+(zn+vntn)2,整理后得ans=(x12+……+xn2)+(y12+……+yn2)+(z12+……+zn2)+(2z1v1t1+v12t12+2z2v2t2+v22t22+……+2znvntn+vn2tn2),也就是说求ans最小,就要让后的最小。

#include<cstdio>
#include<algorithm>
const int maxn=300+5;
typedef long long ll;
struct jew{int z,v,id;
};
jew g[maxn];
int n;
ll ass=0;
ll f(int p,jew x){p--;return 1ll*p*p*x.v*x.v+2ll*p*x.v*x.z;
}
void sofa(int x){int j;for(int i=1;i<=n;i++){if(g[i].id==x){j=i;break;}}for(int i=1;i<=n;i++){if(f(i,g[i])+f(j,g[j]) > f(i,g[j])+f(j,g[i])){printf("%d %d\n",i,j);std::swap(g[i],g[j]);sofa(g[j].id);//Lsofa(x);//Rreturn;}}
}
int main(){scanf("%d",&n);int x,y,z,v;for(int i=1;i<=n;i++){scanf("%d%d%d%d",&x,&y,&z,&v);ass+=1ll*x*x+1ll*y*y+1ll*z*z;g[i]=(jew){z,v,i};}sofa(1);for(int i=1;i<=n;i++)ass+=f(i,g[i]);printf("%lld\n",ass);return 0;
}

仲舟原创,未经允许不得转载。

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

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

    11.无向图删边问题 原题:https://ac.nowcoder.com/acm/contest/11257/C 问题:有n个顶点的无向完全连通图,求删除多少个三角形后,剩余边数可以比n小,并输出所 ...

  2. 解密动态规划:从子问题到最优解的奇妙算法

    目录 引言: 一. 动态规划的基本思想 二. 问题分析与状态定义 三. 状态转移方程的建立 四. 自底向上的求解过程 五. 优化与进阶技巧 六. 实际应用案例 结论: 引言: 动态规划(Dynamic ...

  3. Dijkstra(迪杰斯特拉)算法(C语言)

    今天学了下数据结构,最近国旗班最后一周训练着实使我没什么时间写博客,这也算是挤出点时间把迪杰斯特拉算法好好整了整,我尽量把这个清晰的写出来.这里也参考大佬的代码让我对程序大体有了思路,也算是站在巨人的 ...

  4. JavaWeb之面向故事学习

    JavaWeb之面向故事学习 前言 由于花太多的时间在算法上,项目完全不了解,再加上学校恶心排课压缩课时,致使毫无项目基础的我听课跟听天书一样.虽然讲清楚了如何写,但我完全不知道"为何写&q ...

  5. suffix tree

    文章出处:http://www.cnblogs.com/snowberg/archive/2011/10/21/2468588.html 3   What is a Suffix Tree Suffi ...

  6. 会议交流 | 如何提升推荐系统的可解释性?——DataFunSummit2022知识图谱在线峰会...

    背景介绍 知识图谱及特征学习结合智能推荐,可解决数据稀疏性及冷启动问题,更好的提升推荐决策场的准确性.多样性及可解释性,进而提升各个场景的推荐决策效率和体验. 3月12日13:30-16:50,在Da ...

  7. NOIP2018最终成绩(一等奖名单)

    CCF NOIP2018复赛提高组一等奖获奖名单 CCF NOIP2018复赛普及组一等奖获奖名单 藤校获奖情况 提高组(高中组) [一等奖] 许可  罗思远(两位同学均为精品初中初二学生) [二等奖 ...

  8. 【LLM大模型】模型和指令微调方法

    note Hugging Face 的 PEFT是一个库(LoRA 是其支持的技术之一,除此之外还有Prefix Tuning.P-Tuning.Prompt Tuning),可以让你使用各种基于 T ...

  9. NLP 论文领读|无参数机器翻译遇上对比学习:效率和性能我全都要!

    欢迎关注「澜舟论文领读」专栏!关注"澜舟科技"公众号探索更多 NLP 前沿论文! 本期分享者:付宇 澜舟科技算法研究实习生,天津大学硕士二年级,研究方向为检索增强机器翻译.文本摘要 ...

最新文章

  1. POJ-1724 深搜剪枝
  2. Django 基本使用及目录结构
  3. 产品更新教程-钉钉宜搭版本:手写签名和定位组件来了
  4. 听说版本会说话,你相信吗?
  5. drbd实现mysql地热备_heartheartbeat+drbd+mysql主库热备
  6. python isodd奇偶_Python这些位运算的妙用,绝对让你大开眼界
  7. Docker:Redis启动命令
  8. wireshark 十六进制过滤_Wireshark过滤表达式大全
  9. 将项目绑定https协议
  10. java 当前日期 所在周_关于Java的小工具(计算当前日期所在周的区间)
  11. c++ protected 访问限定
  12. MAC右键菜单踩坑——右键在当前目录打开终端
  13. 一个月薪 12000 的北京程序员的真实生活
  14. 【问题思考】二重积分积分限上的x和积分内部的x有什么区别?【几何直观】
  15. 揭秘郭盛华的真实收入,事实和你想的真不一样
  16. 仿微信朋友圈,文字展开全文,全文收起功能
  17. [深度学习] ncnn安装和调用基础教程
  18. php微信退款 v3版,微信支付-JSAPI支付V3-查询退款
  19. 【使用 arm-poky-linux-gnueabi-gcc -v 指令可以查看 gcc 版本时报错】
  20. 渗透攻防必备工具(基础篇,收藏起来)

热门文章

  1. 【3】SCI易中期刊推荐——人工智能领域(中科院1区)
  2. 图像处理函数im2bw详解
  3. LeetCode 1011. Capacity To Ship Packages Within D Days(python)
  4. 修改QQ版本号,解决QQ版本过低
  5. Hadoop常用端口web界面
  6. 你需要提供管理员权限才能删除文件夹
  7. mysql 表组是什么_数据库中属性组究竟是什么含义?
  8. Anaconda找回默认源
  9. 如何让直播延迟低体验更好?做好这4点就可以了
  10. 鸿蒙TouchEvent已实现单击、连续、长按功能,安卓也可以模仿着原理实现(网上绝大多未实现不动长按),这个支持不动长按事件