算法做题记录

一、递推

95.费解的开关

#include<iostream>
#include<cstring>
using namespace std;const int N = 8;char a[N][N],s[N][N];
int T;
int ans=20,cnt;
int dir[5][2]={1,0,-1,0,0,1,0,-1,0,0};void turn(int x,int y)
{for(int i=0;i<5;i++){int xx = x+dir[i][0];int yy = y+dir[i][1];if(xx<0 || yy<0 || xx>=5 || yy>=5) continue;if(a[xx][yy]=='0') a[xx][yy]='1';else a[xx][yy]='0';}cnt++;
}void solve()
{ans = 10;for(int i=0;i<5;i++) cin>>s[i];for(int i=0;i< 1<<5 ;i++){cnt=0;memcpy(a,s,sizeof a);for(int j=0;j<5;j++){if((i>>j)&1) turn(0,j);}for(int j=1;j<5;j++){for(int k=0;k<5;k++){if(a[j-1][k]=='0') turn(j,k);}}for(int j=0;j<5;j++){if(a[4][j]!='1') break;else if(j==4) ans=min(ans,cnt);}}if(ans<=6) cout<<ans<<endl;else cout<<"-1"<<endl;
}int main()
{cin>>T;while(T--)solve();return 0;
}

二、递归

1.数的遍历

一个二叉树,树中每个节点的权值互不相同。
现在给出它的后序遍历和中序遍历,请你输出它的层序遍历。
image.png

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;const int N = 100;int n;
int a[N],b[N],p[N];
int l[N],r[N],tr[N];int build(int al,int ar,int bl,int br)
{if(al>ar) return -1;int val = a[ar];int pos = p[val];l[val] = build(al,al+pos-bl-1,bl,pos-1);r[val] = build(al+pos-bl,ar-1,pos+1,br);return val;
}void bfs()
{queue<int>q;q.push(a[n]);while(!q.empty()){int x = q.front();q.pop();cout<<x<<" ";if(l[x]!=-1) q.push(l[x]);if(r[x]!=-1) q.push(r[x]);}
}int main()
{memset(l,-1,sizeof l);memset(r,-1,sizeof r);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i],p[b[i]]=i;build(1,n,1,n);bfs();return 0;
}

2.约数之和

假设现在有两个自然数 A和 B,S 是 A B A^B AB的所有约数之和。
请你求出 S mod 9901的值是多少。

解:

  1. 先将 A A A做质因数分解, A = p 1 t 1 ⋅ p 2 t 2 ⋅ . . . ⋅ p k t k A = p_1^{t_1} \cdot p_2^{t_2} \cdot ... \cdot p_k^{t_k} A=p1t1p2t2...pktk

A 的所有约数之和为:
( p 1 0 + p 1 1 + . . . + p 1 t 1 ) × ( p 2 0 + p 2 1 + . . . + p 2 t 2 ) × . . . × ( p k 0 + p k 1 + . . . + p k t k ) (p_1^0 + p_1^1+...+p_1^{t_1}) \times (p_2^0 + p_2^1+...+p_2^{t_2}) \times ... \times (p_k^0 + p_k^1+...+p_k^{t_k}) (p10+p11+...+p1t1)×(p20+p21+...+p2t2)×...×(pk0+pk1+...+pktk)

  1. 那么, A B = p 1 t 1 B ⋅ p 2 t 2 B ⋅ . . . ⋅ p k t k B A^B = p_1^{t_1B} \cdot p_2^{t_2B} \cdot ... \cdot p_k^{t_kB} AB=p1t1Bp2t2B...pktkB

A^B 的所有约数之和为:
( p 1 0 + p 1 1 + . . . + p 1 t 1 B ) × ( p 2 0 + p 2 1 + . . . + p 2 t 2 B ) × . . . × ( p k 0 + p k 1 + . . . + p k t k B ) (p_1^0 + p_1^1+...+p_1^{t_1B}) \times (p_2^0 + p_2^1+...+p_2^{t_2B}) \times ... \times (p_k^0 + p_k^1+...+p_k^{t_kB}) (p10+p11+...+p1t1B)×(p20+p21+...+p2t2B)×...×(pk0+pk1+...+pktkB)

  1. s u m ( p , k ) = p 0 + p 1 + . . . + p k sum(p,k) = p^0 + p^1 + ... + p^k sum(p,k)=p0+p1+...+pk

k为奇数: s u m ( p , k ) = ( p k 2 + 1 + 1 ) ⋅ s u m ( p , k 2 ) sum(p,k) = (p^{\frac{k}{2}+1}+1) \cdot sum(p,\frac{k}{2}) sum(p,k)=(p2k+1+1)sum(p,2k)
k为偶数: s u m ( p , k ) = s u m ( p , k − 1 ) + p k sum(p,k)=sum(p,k-1) + p^k sum(p,k)=sum(p,k1)+pk

代码:

#include<iostream>
using namespace std;const int MOD = 9901;int a,b;
int ans=1;int qmi(int a,int b)
{int res = 1;a %= MOD;while(b){if(b&1) res = res*a % MOD;a = a*a % MOD;b >>= 1;}return res;
}int sum(int p,int k)
{if(k==0) return 1;if(k&1) return (qmi(p,k/2+1)+1)*sum(p,k/2) % MOD;else return qmi(p,k)+sum(p,k-1) % MOD;
}int main()
{cin>>a>>b;for(int i=2;i<=a;i++){int s = 0;while(a%i==0){a /= i;s++;}if(s) ans = ans*sum(i,b*s) % MOD;}if(!a) puts("0");else cout<<ans<<endl;return 0;
}

3.分形之城

市的规划在城市建设中是个大问题。
不幸的是,很多城市在开始建设的时候并没有很好的规划,城市规模扩大之后规划不合理的问题就开始显现。
而这座名为 Fractal 的城市设想了这样的一个规划方案,如下图所示:
image.png
当城区规模扩大之后,Fractal 的解决方案是把和原来城区结构一样的区域按照图中的方式建设在城市周围,提升城市的等级。
对于任意等级的城市,我们把正方形街区从左上角开始按照道路标号。
虽然这个方案很烂,Fractal 规划部门的人员还是想知道,如果城市发展到了等级 N,编号为 A 和 B 的两个街区的直线距离是多少。
街区的距离指的是街区的中心点之间的距离,每个街区都是边长为 10 米的正方形。

思路:先计算数字A,B在n级城市的哪一块,再计算在n-1级城市的x,y坐标,递归综合得到A、B的坐标。
难点:坐标变换。
第一块:xy坐标互换;
第二块、第三块:无变化;
第四块:先逆时针旋转90°,再y坐标对称变换。

#include<iostream>
#include<cmath>
using namespace std;typedef long long ll;
typedef pair<ll,ll>PLL;ll T;
ll n,a,b;PLL get(ll n,ll s)
{if(n==0) return {0,0};ll len = 1 << (n-1);ll sum = len*len;PLL p = get(n-1,s%sum);ll x = p.first , y = p.second;ll block = s / sum;if(block==0) return {y,x};else if(block==1) return {x,y+len};else if(block==2) return {x+len,y+len};else if(block==3) return {len*2 - 1 - y , len - 1 - x};
}void solve()
{cin>>n>>a>>b;PLL pa = get(n,a-1);PLL pb = get(n,b-1);double dx = pa.first - pb.first;double dy = pa.second - pb.second;printf("%.0lf\n",sqrt(dx*dx+dy*dy)*10);
}int main()
{cin>>T;while(T--)solve();return 0;
}

3.并查集

1.T333099 连通块中点的数量

给定一个包含 n 个点( 编号为 1~n )的无向图,初始时图中没有边。
现在要进行 m
个操作,操作共有三种:
1.C a b,在点a和点b之间连一条边,a和b可能相等;
2.Q1 a b,询问点a和点b是否在同一个连通块中,a
b 可能相等;
3.Q2 a,询问点 a 所在连通块中点的数量;

#include<iostream>
using namespace std;const int N = 1000005;int n,m;
string op;
int u,v;
int f[N],w[N];int Find(int x)
{if(f[x]!=x) f[x]=Find(f[x]);return f[x];
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++) f[i]=i,w[i]=1;for(int i=1;i<=m;i++){cin>>op;if(op=="C"){cin>>u>>v;u = Find(u);v = Find(v);// 一定要先判断两个点是否在一个集合,否则数量会出错误(翻倍)if(u!=v){w[v]+=w[u];f[u]=v;}}else if(op=="Q1"){cin>>u>>v;if(Find(u)==Find(v))puts("Yes");else puts("No");}else{cin>>u;u = Find(u);cout<<w[u]<<endl;}}return 0;
}

数量

2.自动程序分析

在实现程序自动分析的过程中,常常需要判定一些约束条件是否能被同时满足。
考虑一个约束满足问题的简化版本:假设 x 1 , x 2 , x 3 , ⋯ x_1,x_2,x_3,\cdots x1,x2,x3, 代表程序中出现的变量,给定 n n n 个形如 x i = x j x_i=x_j xi=xj x i ≠ x j x_i\neq x_j xi=xj 的变量相等/不等的约束条件,请判定是否可以分别为每一个变量赋予恰当的值,使得上述所有约束条件同时被满足。例如,一个问题中的约束条件为: x 1 = x 2 , x 2 = x 3 , x 3 = x 4 , x 4 ≠ x 1 x_1=x_2,x_2=x_3,x_3=x_4,x_4\neq x_1 x1=x2,x2=x3,x3=x4,x4=x1,这些约束条件显然是不可能同时被满足的,因此这个问题应判定为不可被满足。
现在给出一些约束满足问题,请分别对它们进行判定。

思路:
先用并查集连通所有等式,再判断所有不等式,是否有不等式两边元素相等的,若有则不满足。

知识点:
离散化:

int s(int x)
{if(!m.count(x)) m[x]=++idx;return m[x];
}
#include<iostream>
#include<map>
#include<vector>
using namespace std;const int N = 200005;
typedef pair<int,int>PII;int T;
int n,u,v,e;
int f[N];
PII vec[N];
map<int,int>m;
int idx;int s(int x)
{if(!m.count(x)) m[x]=++idx;return m[x];
}int Find(int x)
{if(x!=f[x]) f[x]=Find(f[x]);return f[x];
}void solve()
{scanf("%d", &n);for(int i=1;i<=2*n;i++) f[i]=i;int cnt = 0;idx=0;m.clear();for(int i=1;i<=n;i++){scanf("%d%d%d", &u, &v, &e);u = s(u) , v = s(v);if(e==1){u = Find(u);v = Find(v);f[u]=v;}else vec[++cnt] = {u,v};}for(int i=1;i<=cnt;i++){u = vec[i].first;v = vec[i].second;if(Find(u)==Find(v)){puts("NO");return;}}puts("YES");
}int main()
{scanf("%d", &T);while(T--)solve();return 0;
}

3.银河英雄传说

有一个划分为 N列的星际战场,各列依次编号为 1,2,…,N。
有 N 艘战舰,也依次编号为 1,2,…,N,其中第 i 号战舰处于第 i 列。
有 T条指令,每条指令格式为以下两种之一:

  1. M i j,表示让第 i 号战舰所在列的全部战舰保持原有顺序,接在第 j 号战舰所在列的尾部。
  2. C i j,表示询问第 i号战舰与第 j 号战舰当前是否处于同一列中,如果在同一列中,它们之间间隔了多少艘战舰。

现在需要你编写一个程序,处理一系列的指令。

解析:
用 f 数组记录战舰之间的连通性(是否在一列), sz 数组记录第 i 列有多少量战舰(即维护集合元素个数), d 数组维护结点 i 离其父节点的距离(初始所有结点都是根节点,d 均为0)
每次合并时(以将pa结点挂在pb上为例),只需将d[pa] = sz[pb] 即可完成操作。但还需更新 pa 所有子结点的 d 值,每个子结点 d 值需要增加的量为其父节点的 d 值(在Find函数中实现)

#include<iostream>
using namespace std;const int N = 100005;int n;
char op;
int a,b;
int f[N],d[N],sz[N];int Find(int x)
{if(f[x]!=x){int root = Find(f[x]);d[x] += d[f[x]];f[x] = root;}return f[x];
}int main()
{for(int i=1;i<N;i++) f[i]=i,sz[i]=1;cin>>n;for(int i=1;i<=n;i++){cin>>op>>a>>b;int pa = Find(a) , pb = Find(b);if(op=='M' && pa!=pb) //注意!必须在不同集合上才合并。{d[pa] = sz[pb];f[pa] = pb;sz[pb] += sz[pa];}else if(op=='C'){if(pa!=pb) puts("-1");else cout<<max(0,abs(d[a]-d[b])-1)<<endl;}}return 0;
}

4.食物链

动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。
A 吃 B,B 吃 C,C 吃 A。
现有 N 个动物,以 1∼N编号。
每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是 1 X Y,表示 X 和 Y 是同类。
第二种说法是 2 X Y,表示 X 吃 Y。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。
当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

  1. 当前的话与前面的某些真的话冲突,就是假话;
  2. 当前的话中 X 或 Y 比 N 大,就是假话;
  3. 当前的话表示 X 吃 X,就是假话。

你的任务是根据给定的 N 和 K 句话,输出假话的总数。

思路:用 f 数组记录动物间是否有联系,d 数组表示第 i 个结点到父节点的距离。
f[x] == f[y] : x 与 y 有联系。
d[y] = d[x] + 1 X可以吃Y
对于每句话,需要判断
1.X,Y之间之前是否有联系,如果有联系且与这句话相悖,ans++;
2.如果X,Y之间没有联系,那么
如果X与Y是同类,那么将X挂到Y上,并且更新PX离PY的距离为 d[y] - d[x] ,即表示X与Y为同类。
若果X可以吃Y,那么将X挂到Y上,并且更新PX离PY的距离为d[y] - d[x] + 1,表示X可以吃Y。

#include<iostream>
using namespace std;const int N = 100005;int n,m;
int op,x,y;
int f[N],d[N];
int ans;int Find(int x)
{if(f[x]!=x){int root = Find(f[x]);d[x] += d[f[x]];f[x] = root;}return f[x];
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++) f[i]=i;for(int i=1;i<=m;i++){cin>>op>>x>>y;if(x>n || y>n){ans++;continue;}int px = Find(x) , py = Find(y);if(op==1){if(px == py && (d[x]-d[y])%3) ans++;else if(px != py){d[px] = d[y] - d[x];f[px] = py;}}else{if(px == py && (d[x]-d[y]-1)%3) ans++;else if(px != py){d[px] = d[y] - d[x] + 1;f[px] = f[py];}}}cout<<ans<<endl;return 0;
}

5.奇偶游戏

image.png
image.png
思路:
设 s 为初始数组的前缀和数组,若 l ~ r 和为奇数,则 s[l-1] 与 s[r] 的奇偶性不同,反之则相同。
利用数组 d 来确定每两个结点之间的关系,d[x]-d[y] mod 2 == 1 则代表奇偶性不同,反之相同。

#include<iostream>
#include<map>
using namespace std;const int N = 100005;int n,m;
map<int,int>ma;
int idx;
int l,r;
string op;
int f[N],d[N];
int ans;
int x,y,px,py;int S(int x)
{if(!ma.count(x)) ma[x]=++idx;return ma[x];
}int Find(int x)
{if(x!=f[x]){int root = Find(f[x]);d[x] += d[f[x]];f[x] = root;}return f[x];
}int main()
{cin>>n>>m;for(int i=1;i<N;i++) f[i]=i;for(int i=1;i<=m;i++){cin>>l>>r>>op;if(ans>0) continue;x = S(l-1) , y = S(r);px = Find(x) , py = Find(y);if(op == "even"){if(px == py && (d[x]-d[y])%2) ans = i;else if(px != py){d[px] = d[x]^d[y];f[px] = py;}}else{if(px == py && (d[x]-d[y])%2 == 0) ans = i;else if(px != py){d[px] = d[x]+d[y]+1;f[px] = py;}}}if(!ans) cout<<m<<endl;else cout<<ans-1<<endl;return 0;
}

四、单调队列

1.滑动窗口

image.png
运用了双端队列
求最大值时,在每次插入新元素之前,先判断队头是否离开窗口,再将队尾所有小于当前值的元素pop出去。

#include<iostream>
#include <deque>
using namespace std;const int N = 300005;int n,m;
int a[N];
deque<int>q;int main()
{cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++){if(!q.empty() && i-q.front()>=m) q.pop_front();while(!q.empty() && a[i]<a[q.back()]) q.pop_back();q.push_back(i);if(i>=m) cout<<a[q.front()]<<" ";}cout<<endl;while(!q.empty()) q.pop_back();for(int i=1;i<=n;i++){if(!q.empty() && i-q.front()>=m) q.pop_front();while(!q.empty() && a[i]>a[q.back()]) q.pop_back();q.push_back(i);if(i>=m) cout<<a[q.front()]<<" ";}return 0;
}

2.最大自序和

image.png

#include<iostream>
#include<deque>
using namespace std;const int N = 300005;int n,m;
int a[N],s[N];
deque<int>q;
int ans=-3e9;int main()
{cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i];for(int i=1;i<=n;i++){if(!q.empty() && i-q.front()>=m) q.pop_front();while(!q.empty() && s[i-1]-s[q.back()-1]<=0) q.pop_back(); //错误 之前是s[i]-s[tail];q.push_back(i);ans = max(ans,s[i]-s[q.front()-1]);}cout<<ans<<endl;return 0;
}

3.单调栈

image.png

#include<iostream>
#include<stack>
using namespace std;const int N = 3000005;int n;
int a[N],f[N];
stack<int>s;int main()
{scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=n;i>=1;i--){while(!s.empty() && a[i]>=a[s.top()]) s.pop();if(s.empty()) f[i]=0;else f[i]=s.top();s.push(i);}for(int i=1;i<=n;i++) printf("%d ",f[i]);return 0;
}

4.烽火传递

image.png

#include<iostream>
#include<deque>
using namespace std;const int N = 200005;int n,m;
int a[N],f[N];
deque<int>q;
int ans = 1e9;int main()
{cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];q.push_back(0);for(int i=1;i<=n;i++){if(!q.empty() && i-q.front()>m) q.pop_front();f[i] = f[q.front()] + a[i];while(!q.empty() && f[i]<f[q.back()]) q.pop_back();q.push_back(i); }for(int i=0;i<m;i++) ans=min(ans,f[n-i]);cout<<ans<<endl;return 0;
}

五、博弈论

1. Nim游戏

#include<iostream>
using namespace std;int n;
int a;
int ans;int main()
{cin>>n;for(int i=1;i<=n;++i){cin>>a;ans^=a;}if(ans) puts("Yes");else puts("No");return 0;
}

2. 台阶-Nim游戏

#include<iostream>
using namespace std;typedef long long ll;
const int N = 100005;ll a[N],res;
int n;int main()
{cin>>n;for(int i=1;i<=n;++i) cin>>a[i];for(int i=1;i<=n;++i){if(i%2) res^=a[i];}if(res) puts("Yes");else puts("No");return 0;
}

3. 集合-Nim游戏

博弈论进阶之SG函数

#include<iostream>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;int n,m,a;
int res;
int s[105],f[100005];//当sg值为0时,为必败状态,因为他下一次无法转化为sg为0的状态,即无法转化成必败状态
//当sg值为非0时,为必胜状态,他的子集有sg为0的状态,因此可以转移为必败状态int sg(int x)
{if(f[x]!=-1) return f[x];set<int>S;//装可以有x转移到的的状态的sg值for(int i=0;i<m;++i){if(x>=s[i]) S.insert(sg(x-s[i]));}//Mex函数,返回集合S中未出现的最小值for(int i=0;;i++){if(!S.count(i)) return f[x]=i;}
}int main()
{memset(f,-1,sizeof f);cin>>m;for(int i=0;i<m;++i) cin>>s[i];cin>>n;for(int i=0;i<n;++i){cin>>a;res^=sg(a);}if(res) puts("Yes");else puts("No");return 0;
}

4. 拆分-Nim游戏

#include<iostream>
#include<algorithm>
#include<cstring>
#include<unordered_set>
using namespace std;const int N = 105;
int n;
int f[N];
int res;int sg(int x)
{if(f[x]!=-1) return f[x];unordered_set<int>S;for(int i=0;i<x;i++){for(int j=0;j<x;j++){S.insert(sg(i)^sg(j));}}for(int i=0;;i++){if(!S.count(i)) return f[x]=i;}
}int main()
{memset(f,-1,sizeof f);cin>>n;for(int i=1;i<=n;++i){int x;cin>>x;res^=sg(x);}if(res) puts("Yes");else puts("No");return 0;
}

5.牛客

六、线性dp(进阶指南)

271. 杨老师的照相排列

用f[a][b][c][d][e]表示第1~5排人数为abcde的方案数。
第一排人数一定大于第二排人数,第二排人数一定大于第三排人数…
求f[a][b][c][d][e]时,可以假设将最后一个人放到第12345排,放到第一排时,需要保证a至少为1,且a-1>=b。意思为将最后一人放到第一排后,第一排至少有1个人,且放之前第一排人数大于第二排人数(为有效状态)。

#include<iostream>
#include<cstring>
using namespace std;const int N = 32;
typedef long long LL;int n;
int s[N];
LL f[N][N][N][N][N];int main()
{f[0][0][0][0][0]=1;for(int a=0;a<N;a++)for(int b=0;b<=a;b++)for(int c=0;c<=b;c++)for(int d=0;d<=c;d++)for(int e=0;e<=d;e++){if(a && a-1>=b) f[a][b][c][d][e]+=f[a-1][b][c][d][e];if(b && b-1>=c) f[a][b][c][d][e]+=f[a][b-1][c][d][e];if(c && c-1>=d) f[a][b][c][d][e]+=f[a][b][c-1][d][e];if(d && d-1>=e) f[a][b][c][d][e]+=f[a][b][c][d-1][e];if(e) f[a][b][c][d][e]+=f[a][b][c][d][e-1];}while(cin>>n && n){memset(s,0,sizeof s);   // 每次需要清空s,因为此次数据排数可能没以前多,会保留多于排的人数for(int i=1;i<=n;i++) cin>>s[i];cout<<f[s[1]][s[2]][s[3]][s[4]][s[5]]<<endl;}return 0;
}

272. 最长公共上升子序列

#include<iostream>
using namespace std;const int N = 3005;int n;
int a[N],b[N];
int f[N][N];    //以b[j]结尾的最大上升公共子序列长度int main()
{cin>>n;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i];for(int i=1;i<=n;i++){int maxv = 0;for(int j=1;j<=n;j++){f[i][j]=f[i-1][j];  //不选a[i]的情况//选a[i]的情况// if(a[i]==b[j])// {//     int maxv = 0;   //找以b[1~j-1]结尾的最大值//     for(int k=1;k<j;k++)//     {//         //满足上升子序列才更新//         if(b[j]>b[k]) maxv = max(maxv,f[i-1][k]);//     }//     f[i][j]=max(f[i][j],maxv+1);// }if(a[i]>b[j-1]) maxv = max(maxv,f[i-1][j-1]);   //因为该情况a[i]==b[j]if(a[i]==b[j]) f[i][j]=max(f[i][j],maxv+1);}}int ans =0;for(int i=1;i<=n;i++) ans=max(ans,f[n][i]);cout<<ans<<endl;return 0;
}

277. 饼干

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;const int N = 35, M = 5005;int n,m;
int f[N][M];    //代表前i个小朋友分配j个糖果的最小怒气和
int ans[N],s[N];
struct Greedy
{int id,x;bool operator<(const Greedy a)const{return x>a.x;}
}g[N];int main()
{cin>>n>>m;for(int i=1;i<=n;i++){cin>>g[i].x;g[i].id = i;}sort(g+1,g+n+1);for(int i=1;i<=n;i++) s[i]=s[i-1]+g[i].x;memset(f,0X3f,sizeof f);f[0][0]=0;for(int i=1;i<=n;i++){for(int j=i;j<=m;j++){//假如最优分配方案中没有人有一个饼干,那么把他们每个人的饼干都减1,答案不变。f[i][j]=f[i][j-i];//假设有k个人分配得一个饼干for(int k=1;k<=i && k<=j;k++){f[i][j]=min(f[i][j],f[i-k][j-k]+(s[i]-s[i-k])*(i-k));}}}cout<<f[n][m]<<endl;//回溯,h代表所有人饼干数减1的次数int h = 0;for(int i=n,j=m;i && j;){if(j>=i && f[i][j]==f[i][j-i]) j-=i,h++;else{for(int k=1;k<=i && k<=j;k++){if(f[i][j]==f[i-k][j-k]+(s[i]-s[i-k])*(i-k)){for(int u=i-k+1;u<=i;u++)ans[g[u].id]=1+h;i-=k,j-=k;break;}}}}for(int i=1;i<=n;i++) cout<<ans[i]<<" ";return 0;
}

七、背包问题

278. 数字组合

把每个数字看成一个价值为i的物品,求总价值为m的方案数。(01背包)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;const int N = 100005;int n,m;
int f[N],a[N];int main()
{cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];f[0]=1;for(int i=1;i<=n;i++){for(int j=N-1;j>=a[i];j--){f[j]+=f[j-a[i]];}}cout<<f[m]<<endl;return 0;
}

279. 自然数拆分

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;// 把每个数字看成一个价值为1,体积为1的物品
// 做完全背包问题求方案数const int N = 100005, MOD = 2147483648;
typedef long long ll;int n;
ll f[N];int main()
{cin>>n;f[0]=1;for(int i=1;i<=n;i++){for(int j=i;j<=n;j++){f[j] = (f[j]+f[j-i])%MOD;}}cout<<f[n]-1<<endl;return 0;
}

280. 陪审团

三维dp+回溯

#include<iostream>
#include<cstring>
using namespace std;const int N = 205, M = 25, base = 400;int n,m;
int p[N],d[N];
int f[N][M][805];   //前i个人选j个且abs(d-p)=k的最大d+p值
bool st[N];
int T;int main()
{while(cin>>n>>m && (n||m)){T++;memset(st, 0, sizeof st);memset(f,-0X3f,sizeof f);f[0][0][base]=0;    //差值为0的f为0for(int i=1;i<=n;i++) cin>>p[i]>>d[i];for(int i=1;i<=n;i++){for(int j=0;j<=m;j++){for(int k=0;k<805;k++){// 如果不选第i个人f[i][j][k]=f[i-1][j][k];int t = k-(p[i]-d[i]);if(t<0 || t>805) continue;if(j<1) continue;f[i][j][k]=max(f[i][j][k],f[i-1][j-1][t]+d[i]+p[i]);}}}int i=n,j=m,k=0;while(f[n][m][base-k]<0 && f[n][m][base+k]<0) k++;if(f[n][m][base-k]>f[n][m][base+k]) k=base-k;else k=base+k;while(j){if(f[i][j][k]==f[i-1][j][k]) i--;   //  如果第i个人可以不选else{st[i]=true;k-=p[i]-d[i];i--,j--;}}int sp=0,sd=0;for(int i=1;i<=n;i++){if(st[i]) sp+=p[i],sd+=d[i];}printf("Jury #%d\n",T);printf("Best jury has value %d for prosecution and value %d for defence:\n",sp,sd);for(int i=1;i<=n;i++) if(st[i]) printf("%d ",i);puts("\n");}return 0;
}

281. 硬币

#include<iostream>
#include<cstring>
using namespace std;const int N = 100005;int n,m;
int a[N],c[N];
int f[N],g[N];  //f[i]代表i元是否可以被凑成int main()
{while(cin>>n>>m && n && m){memset(f,0,sizeof f);f[0]=1;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>c[i];for(int i=1;i<=n;i++){//g[j]表示在处理第i种硬币时,凑成面值j至少需要第i中硬币的个数memset(g,0,sizeof g);for(int j=a[i];j<=m;j++){//如果j还没有被凑出,且j-a[i]已经被凑出,且凑出j-a[i]后还剩余第i中钱币。if(!f[j] && f[j-a[i]] && g[j-a[i]]<c[i]){g[j] = g[j-a[i]]+1;f[j] = 1;}}}int ans = 0;for(int i=1;i<=m;i++)if(f[i]) ans++;cout<<ans<<endl;}return 0;
}

八、区间dp

282. 石子合并

#include<iostream>
#include<cstring>
using namespace std;const int N = 1005;int n;
int a[N],s[N];
int f[N][N];int main()
{cin>>n;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];memset(f,0X3f,sizeof f);for(int i=1;i<=n;i++) f[i][i]=0;for(int len=2;len<=n;len++){for(int l=1;l+len-1<=n;l++){int r = l+len-1;for(int k=l;k<r;k++){f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);}}}cout<<f[1][n]<<endl;return 0;
}

283. 多边形

#include<iostream>
#include<cstring>
using namespace std;const int N = 105 , INF = 0X3f3f3f3f;int n;
int num[N];
char op[N];
int f[N][N],g[N][N];
int ans;int main()
{cin>>n;for(int i=1;i<=n;i++){cin>>op[i]>>num[i];op[i+n]=op[i];num[i+n]=num[i];}for(int len=1;len<=n;len++){//for(int l=1;l<=n;l++)for(int l=1;l+len-1<=2*n;l++){int r = l+len-1;if(len==1){f[l][r]=g[l][r]=num[l];continue;}f[l][r]=-INF,g[l][r]=INF;for(int k=l;k<r;k++){char ch = op[k+1];int minl = g[l][k],minr = g[k+1][r];int maxl = f[l][k],maxr = f[k+1][r];if(ch=='t'){f[l][r]=max(f[l][r],maxl+maxr);g[l][r]=min(g[l][r],minl+minr);}else{int x1 = minl * minr;   //  负数乘负数int x2 = maxl * maxr;   //  正数乘正数int x3 = maxl * minr;   //  int x4 = minl * maxr;f[l][r] = max(f[l][r],max(max(x1,x2),max(x3,x4)));g[l][r] = min(g[l][r],min(min(x1,x2),min(x3,x4)));}}}}for(int i=1;i<=n;i++) ans = max(ans,f[i][i+n-1]);cout<<ans<<endl;for(int i=1;i<=n;i++)if(ans==f[i][i+n-1]) cout<<i<<" ";return 0;
}

284. 金字塔

#include<iostream>
#include<cstring>
using namespace std;const int N = 305 , MOD = 1e9;
typedef long long ll;int n;
char s[N];
ll f[N][N];int main()
{cin>>(s+1);n = strlen(s+1);for(int len=1;len<=n;len++){for(int l=1;l+len-1<=n;l++){int r = l+len-1;if(len==1) f[l][r]=1;else if(s[l]==s[r]){for(int k=l;k<r;k+=2){//找最后一个子树if(s[k]==s[r])f[l][r] = (f[l][r]+f[l][k]*f[k+1][r-1])%MOD;}}}}cout<<f[1][n]<<endl;return 0;
}

九、树形dp

285. 没有上司的舞会

#include<iostream>
#include<cstring>
using namespace std;const int N = 200005;int n;
int w[N];
int f[N],g[N];  //f[x]代表x去宴会,及其下属的总快乐指数;g[x]为x不去宴会
int l,k,root;
int ne[N],to[N],head[N],idx;
bool st[N];void add(int u,int v)
{idx++;ne[idx]=head[u];to[idx]=v;head[u]=idx;
}void dfs(int x)
{f[x] = w[x];for(int i=head[x];~i;i=ne[i]){int y = to[i];dfs(y);f[x] += g[y];g[x] += max(f[y],g[y]);}
}int main()
{memset(head,-1,sizeof head);cin>>n;for(int i=1;i<=n;i++) cin>>w[i];for(int i=1;i<n;i++){cin>>l>>k;st[l]=true;add(k,l);}for(int i=1;i<=n;i++) if(!st[i]) root = i;dfs(root);cout<<max(f[root],g[root]);return 0;
}

286. 选课

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;const int N = 1005;int n,m;
int w[N];
int x;
int f[N][N];    //以i为根节点,选j门课的最大学分数
int ne[N],head[N],to[N],idx;
bool st[N];
int ans;void add(int u,int v)
{idx++;ne[idx]=head[u];to[idx]=v;head[u]=idx;
}void dfs(int x)
{for(int i=head[x];~i;i=ne[i]){int y = to[i];dfs(y);for(int j=m-1;j>=0;j--){for(int k=0;k<=j;k++){f[x][j]=max(f[x][j],f[x][j-k]+f[y][k]);}}}for(int i=m;i>=1;i--) f[x][i]=f[x][i-1]+w[x];f[x][0]=0;
}int main()
{memset(head,-1,sizeof head);cin>>n>>m;for(int i=1;i<=n;i++){cin>>x>>w[i];add(x,i);}m++;    //默认要选0号课程(虚拟根节点)dfs(0);cout<<f[0][m]<<endl;return 0;
}

287. 积蓄程度

#include<iostream>
#include<cstring>
using namespace std;const int N = 400005 , INF = 0X3f3f3f3f;int T,n;
int ne[N],head[N],to[N],w[N],idx;
int f[N],d[N],deg[N];
int x,y,z;void add(int u,int v,int dis)
{idx++;ne[idx] = head[u];to[idx] = v;w[idx] = dis;head[u] = idx;
}//d[i]代表以root为根,从结点i向下流的最大流量
int dfs_d(int x,int fa)
{if(deg[x]==1) return f[x]=INF;for(int i=head[x];~i;i=ne[i]){int y = to[i];if(y==fa) continue;d[x] += min(w[i],dfs_d(y,x));}return d[x];
}//f[i]代表第i个结点的最大流量
int dfs_f(int x,int fa)
{for(int i=head[x];~i;i=ne[i]){int y = to[i];if(y==fa) continue;if(deg[y]==1) f[y] = min(w[i],f[x]-w[i]);else {f[y] = d[y] + min(f[x]-min(d[y],w[i]),w[i]);dfs_f(y,x);}}return f[x];
}void solve()
{memset(head,-1,sizeof head);memset(deg,0,sizeof deg);memset(f,0,sizeof f);memset(d,0,sizeof d);idx = 0;cin>>n;for(int i=1;i<n;i++){cin>>x>>y>>z;add(x,y,z);add(y,x,z);deg[x]++,deg[y]++;}int root = 1;while(root<=n && deg[root]==1) root++;if(root>n) {cout<<w[1]<<endl;return;}dfs_d(root,-1);f[root] = d[root];dfs_f(root,-1);int ans = 0;for(int i=1;i<=n;i++)ans = max(ans,f[i]);cout<<ans<<endl;
}int main()
{cin>>T;while(T--)solve();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/99518.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在MySQL中查看数据库和表的数据大小

在MySQL中查看数据库和表的数据大小 在管理和维护MySQL数据库时&#xff0c;了解数据库和表的数据大小是非常重要的。这可以帮助您监控数据库的增长、优化性能以及规划存储需求。本博客将介绍如何使用SQL查询来查看MySQL数据库和表的数据大小。 查看MySQL数据库的总数据大小 …

【开发语言】C语言与Python的互操作详解

博主未授权任何人或组织机构转载博主任何原创文章&#xff0c;感谢各位对原创的支持&#xff01; 博主链接 本人就职于国际知名终端厂商&#xff0c;负责modem芯片研发。 在5G早期负责终端数据业务层、核心网相关的开发工作&#xff0c;目前牵头6G算力网络技术标准研究。 博客…

vscode debug python launch.json添加args不起作用

问题 为了带入参数调试python 程序&#xff0c;按照网上搜到的教程配置了lauch.json文件&#xff0c;文件中添加了"args": [“model” “0” “path”] {// 使用 IntelliSense 了解相关属性。 // 悬停以查看现有属性的描述。// 欲了解更多信息&#xff0c;请访问: h…

PCL RANSAC分割提取多个空间圆

目录 一、概述二、代码实现三、结果展示1、原始数据2、提取结果四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、概述 使用PCL分割提取多个空间圆,其核心原理仍然是RANSAC拟合空间圆,这里只是做简单修改…

【广州华锐互动】煤矿设备AR远程巡检系统实现对井下作业的远程监控和管理

煤矿井下作业环境复杂&#xff0c;安全隐患较多。传统的巡检方式存在诸多弊端&#xff0c;如巡检人员难以全面了解井下情况&#xff0c;巡检效率低下&#xff0c;安全隐患难以及时发现和整改等。为了解决这些问题&#xff0c;提高煤矿安全生产水平&#xff0c;越来越多的企业开…

Oracle中LEFT JOIN后AND与WHERE的异同

1、AND 过滤之后再连接 2、WHERE 连接之后再过滤 下面以具体例子来说明&#xff1a; (1)、建表及插入测试数据 --建测试表 create table FACT_TAB ( ID INTEGER,STATUS VARCHAR2(8) ); create table DIM_STATUS ( STSTUS_CLASS VARCHAR2(8),STATUS_CODE VARCHAR2(8),S…

The Sandbox 即将参加韩国区块链周,并带来一系列独家周边活动!

韩国区块链周&#xff08;Korea Blockchain Week&#xff09;即将到来&#xff0c;届时将有成千上万的 NFT 项目、建设者、社区成员、企业家、投资者和爱好者齐聚首尔&#xff0c;分享 Web3 的最新更新和未来愿景。 继成功举办韩流崛起 LAND 销售并宣布多个合作伙伴关系之后&a…

Java低代码开发:jvs-list(列表引擎)功能(一)配置说明

在低代码开发平台中&#xff0c;列表页是一个用于显示数据列表的页面。它通常用于展示数据库中的多条记录&#xff0c;并提供搜索、排序和筛选等功能&#xff0c;以方便用户对数据进行查找和浏览。 jvs-list是jvs快速开发平台的列表页的配置引擎&#xff0c;它和普通的crud 具…

C# winform控件和对象双向数据绑定

实现目的&#xff1a; 控件和对象双向数据绑定 实现结果&#xff1a; 1. 对象值 -> 控件值 2. 控件值 -> 对象值 using System; using System.Windows.Forms;namespace ControlDataBind {public partial class MainForm : Form{People people new People();public Mai…

JavaScript基础语法04——输入输出语法

嗨&#xff0c;大家好&#xff0c;我是雷工。 今天学习JavaScript基础语法&#xff0c;输入输出语法&#xff0c;以下为学习笔记。 1、输出语法&#xff1a; 1.1、alert&#xff08;&#xff09; 作用&#xff1a;界面弹出警告对话框。 示例&#xff1a; <script>aler…

R语言STAN贝叶斯线性回归模型分析气候变化影响北半球海冰范围和可视化检查模型收敛性...

原文链接&#xff1a;http://tecdat.cn/?p24334 像任何统计建模一样&#xff0c;贝叶斯建模可能需要为你的研究问题设计合适的模型&#xff0c;然后开发该模型&#xff0c;使其符合你的数据假设并运行&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 相关视频…

【算法|链表】移除链表元素

算法|链表-移除链表元素 关于链表的介绍以及相关实现操作&#xff0c;见单链表&#xff0c;双链表 leetcode 203 移除链表元素 题意&#xff1a;删除链表中等于给定值 val 的所有节点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1…