T1.前后缀和
题目描述:
小红定义一个长度为偶数的数组的权值为:每两个元素之间依次填入乘号和加号(第一个先填乘号),然后计算结果。例如,输入[1,2,3,4]的权值为1x2+3x4=14。
现在小红拿到了一个长度为奇数的数组,她定义
f
(
i
)
f(i)
f(i)为,若删除第
i
i
i个元素,剩余的元素构成的数组的权值。请你帮小红求出
f
(
1
)
,
f
(
2
)
,
.
.
.
,
f
(
n
)
f(1),f(2),...,f(n)
f(1),f(2),...,f(n)的值。
输入描述
第一行输入一个正整数
n
n
n,代表小红拿到的数组。
第二行输入
n
n
n个正整数
a
i
a_i
ai,代表数组中的元素。
3
≤
n
≤
1
0
5
3 \leq n \leq 10^5
3≤n≤105
1
≤
a
i
≤
1
0
5
1 \leq a_i \leq 10^5
1≤ai≤105
保证
n
n
n是奇数。
输出描述
输出
n
n
n行,第
i
i
i行代表
f
(
i
)
f(i)
f(i)的值。
样例输入
5
1 2 3 4 5
样例输出
26
23
22
17
14
思路:
乍一看以为就是一个简答的模拟,结果用双重for循环严重超时。就只能思考用什么算法:
前缀和+后缀和+奇偶分情况
i=0,f=a[1]*a[2]+a[3]*a[4]
i=1,f=a[0]*a[2]+a[3]*a[4]
i=2,f=a[0]*a[1]+a[3]*a[4]
i=3,f=a[0]*a[1]+a[2]*a[4]
i=4,f=a[0]*a[1]+a[2]*a[3]
通过观察规律可以发现:
(如果把x*x+x*x叫做变相的“和”的话)
当i为偶数时,f=(i-1开始的前缀和)+(i+1开始的后缀和)
所以需要计算下标为奇数的前缀和、后缀和
当i为奇数时,f=(i-2开始的前缀和)+(i+2开始的后缀和)+a[i-1]+a[i+1]
所以需要计算下标为奇数的前缀和、后缀和
总结:需要计算下标为奇数的前缀和、后缀和;然后再分奇偶、分下标越界进行计算
Java实现:
public class TaoTian1 {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int[] a=new int[n];
for(int i=0;i<n;i++){
a[i]=in.nextInt();
}
//计算前缀和数组、后缀和数组
int[] pre=new int[n];
int[] suf=new int[n];
int cur=0;
for(int i=1;i<n;i+=2){
cur+=a[i]*a[i-1];
pre[i]=cur;
}
cur=0;
for(int i=n-2;i>=0;i-=2){
cur+=a[i]*a[i+1];
suf[i]=cur;
}
//分奇偶讨论
for(int i=0;i<n;i++){
int res=0;
if(i%2==0){
if(i-1>=0){
res+=pre[i-1];
}
if(i+1<n){
res+=suf[i+1];
}
}else{
if(i-2>0){
res+=pre[i-2];
}
if(i+2<n-1){
res+=suf[i+2];
}
res+=a[i-1]*a[i+1];
}
System.out.println(res);
}
}
}
T2.树的度数
题目描述:
小红拿到了一个无向图,她准备选择两条边,满足这两条边的4个节点各不相同。小红想知道,有多少种选择边的方案?
输入描述:
第一行输入两个正整数
n
n
n和
m
m
m,代表无向图的节点数量和边的数量。
接下来的
m
m
m行,每行输入2个正整数
u
,
v
u,v
u,v,代表节点
u
u
u和节点
v
v
v有一条边连接。
1
≤
n
,
m
≤
1
0
5
1 \leq n,m \leq 10^5
1≤n,m≤105
1
≤
u
,
v
≤
n
1 \leq u,v \leq n
1≤u,v≤n
保证给定的图不包含重边和自环。
输出描述:
一个整数,代表选择边的方案数。
样例输入:
4 4
1 2
2 3
3 4
4 1
样例输出:
2
说明
方案1:选择第1条边和第3条边。
方案2:选择第2条边和第4条边。
思路:
任意选择两条边的方案数:
C
m
2
C_m^2
Cm2
减去两条边相邻的情况。就是最终的方案数。
如何计算两条边相邻的情况数呢?
两条边相邻是因为连接在同一个节点上,也就是说,一个节点上所有的边都是相邻的。这是无向图,所以需要计算节点的度数,节点的度数为
k
k
k,则该节点就会导致
C
k
2
C_k^2
Ck2中两条边相邻的情况。
Java实现:
public class TaoTian2 {
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
int n=in.nextInt();
int m=in.nextInt();
//记录每一个节点的度数
long[] deg=new long[n];
for(int i=0;i<m;i++){
int u=in.nextInt()-1;
int v=in.nextInt()-1;
deg[u]++;
deg[v]++;
}
long ans=(long)m*(m-1)/2;
for(int i=0;i<n;i++){
ans-=deg[i]*(deg[i]-1)/2;
}
System.out.println(ans);
}
}
T3.回溯
题目描述:
小红拿到了一个长度为
n
n
n的数组。她定义一个子序列是“好的”,当且仅当该子序列所有元素之和为奇数。
现在小红想求出所有“好的”子序列的元素和之和。你能帮帮她吗? 由于答案可能过大,请对
1
0
9
+
7
10^9+7
109+7取模。
定义一个数组的子序列是,数组中取若干元素 (可以不连续) 按原数组顺序形成的新数组。
输入描述:
第一行输入一个正整数
n
n
n,代表小红拿到的数组。
第二行输入
n
n
n个正整数
a
i
a_i
ai,代表数组中的元素。
1
≤
n
≤
1
0
5
1 \leq n \leq 10^5
1≤n≤105
1
≤
a
i
≤
1
0
9
1 \leq a_i \leq 10^9
1≤ai≤109
输出描述:
所有“好的”子序列的元素和之和,答案对
1
0
9
+
7
10^9+7
109+7取余。
样例输入:
3
1 3 2
样例输出:
12
说明:
好的子序列有以下几个:
[1],[3],[1,2],[3,2],答案是1+3+3+5=12
思路:
用回溯算法进行暴力搜索,但是会超时!目前我不知道还可以怎样做,求各位大佬指教!
Java实现:(回溯)
public class TaoTian3 {
public static List<Integer> path;
public static long res;
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n=in.nextInt();
int[] arr=new int[n];
for(int i=0;i<n;i++){
arr[i]=in.nextInt();
}
path=new ArrayList<>();
res=0;
long sum=0;
tranverse(arr,0,sum);
System.out.println((int)(res%(int)(1e9+7)));
}
public static void tranverse(int[] arr,int start,long sum){
if(sum%2!=0){
res+=sum;
}
if(start>=arr.length){
return;
}
for(int i=start;i<arr.length;i++){
path.add(arr[i]);
sum+=arr[i];
tranverse(arr,i+1,sum);
path.remove(path.size()-1);
sum-=arr[i];
}
}
}
T3番外.动态规划
和T3类似,但是可以用动态规划解决:
力扣1524.和为奇数的子数组数目
给你一个整数数组 arr 。请你返回和为 奇数 的子数组数目。
由于答案可能会很大,请你将结果对
1
0
9
+
7
10^9+7
109+7 取余后返回。
示例 1:
输入:
arr = [1,3,5]
输出:
4
解释:
所有的子数组为 [[1],[1,3],[1,3,5],[3],[3,5],[5]] 。
所有子数组的和为 [1,4,9,3,8,5]。
奇数和包括 [1,9,3,5] ,所以答案为 4 。
示例 2 :
输入:
arr = [2,4,6]
输出:
0
解释:
所有子数组为 [[2],[2,4],[2,4,6],[4],[4,6],[6]] 。
所有子数组和为 [2,6,12,4,10,6] 。
所有子数组和都是偶数,所以答案为 0 。
示例 3:
输入:
arr = [1,2,3,4,5,6,7]
输出:
16
思路:
首先定义状态:
dp[i][0]表示以arr[i]结尾的子数组和为奇数的个数;
dp[i][1]表示以arr[i]结尾的子数组和为偶数的个数。
接着是状态转移方程:
我们知道奇数+偶数=奇数;奇数+奇数=偶数,所以当当前元素arr[i]是奇数时:
dp[i][0]=dp[i-1][1]+1;//+1是算上他自己
dp[i][1]=dp[i-1][0];
偶数+奇数=奇数;偶数+偶数=偶数,所以当前元素arr[i]是偶数时:
dp[i][0]=dp[i-1][0];
dp[i][1]=dp[i-1][1]+1;//+1是算上他自己
最后的res就是dp[i][0]之和
Java实现:
class Solution {
public int numOfSubarrays(int[] arr) {
int n=arr.length;
int[][] dp=new int[n][2];
long res=0l;
if(arr[0]%2!=0){//首元素为奇数
dp[0][0]=1;
dp[0][1]=0;
res++;
}else{
dp[0][0]=0;
dp[0][1]=1;
}
for(int i=1;i<n;i++){
if(arr[i]%2!=0){//arr[i]为奇数
dp[i][0]=dp[i-1][1]+1;
dp[i][1]=dp[i-1][0];
}else{
dp[i][0]=dp[i-1][0];
dp[i][1]=dp[i-1][1]+1;
}
res+=dp[i][0];
}
res%=(int)(1e9+7);
return (int)res;
}
}
参考:
https://www.xiaohongshu.com/user/profile/63e06f7a00000000260076f8