输入第一行两个数n , k
第二行n个数, a1 , a2 , a3……… , an
输出一个数,一共有多少台路由器可以收到至少k台不同路由器的信号。
4 4 3 3 3 3
4
#include<iostream> (720)#include<stdio.h> #define MaxN 100010 using namespace std; int N, K; int chafen[MaxN];//差分数组 int main(){ scanf("%d%d", &N, &K); int temp; for(int i=0; i<N; i++){ scanf("%d", &temp); chafen[max(0, i-temp)]++; //最左发送范围 chafen[min(N, i+temp+1)]--; //最右发送范围的下一个路由器 } int sum = 0, ans = 0; for(int i=0; i<N; i++){ sum+=chafen[i]; //还原出原数组第i项 if(sum >= K) ans++; } cout<<ans<<endl; return 0; }
看了这么多题解,根本没看懂,说一下我个人对这道题的理解。
首先python代码如下:
if __name__=='__main__': n,k = list(map(int,input().split())) num = list(map(int,input().split())) res = [] for i in range(n): l = max(0,i-num[i]) r = min(n,i+num[i]+1) res.append((l,r)) dp = [0 for _ in range(n+1)] for i in range(n): dp[res[i][0]] += 1 dp[res[i][1]] -= 1 count = 0 temp = 0 for i in range(len(dp)): temp += dp[i] if temp >= k: count += 1 print(count)
上面主要使用了两个存储:
res 和 dp
res 的作用是记录每个路由器能到达的左边界。
res[i][0] = 0的话,表示路由器i最左边能到达坐标 0 。
也说明了能到达 坐标 1, 2, ... 。但是最右能到达哪呢?这个在res[i][1] 记录着。
dp数组累加表示坐标 i 有几个路由器能到达。
看下面dp怎么算的,大家就能理解这个算法奥秘了。
for i in range(n): dp[res[i][0]] += 1 dp[res[i][1]] -= 1
以
4 4
3 1 3 2 为例。
我把它的res dp 结果输出了。
('res = ', [(0, 4), (0, 3), (0, 4), (1, 4)])
('dp = ', [3, 1, 0, -1, -3])
这个方法还是很巧妙,有知道这种题是属于什么类型的,求告知,感谢!!!
public class Main { public static void main(String[] args) { java.util.Scanner sc = new java.util.Scanner(System.in); int N = sc.nextInt(), K = sc.nextInt(); int[] delta = new int[N + 2]; for (int i = 1; i <= N; ++i) { int r = sc.nextInt(); delta[Math.max(0, i - r)] += 1; delta[Math.min(N + 1, i + r + 1)] -= 1; } int cnt = 0, sum = delta[0]; for (int i = 1; i <= N; ++i) { sum += delta[i]; if (sum >= K) { ++cnt; } } System.out.println(cnt); } }
恕我直言,没一个答案把此题的思路讲清楚,代码我就不贴了,大同小异。此题的的求解关键在于差分数组,不是什么dp。看不懂代码的同学强烈建议搜索差分数组,理解其原理性质后再读代码就很好理解了。
n, k = map(int, input().strip().split()) route = list(map(int, input().strip().split())) area = [] # 计算路由i的信号能到达的左右边界 for i in range(n): left = max(0, i - route[i]) # 路由i能到达的左边界 right = min(n, i + route[i] + 1) # 路由i能到达的右边界(第一个到达不了的路由) area.append((left, right)) """ 对于路由器i,它能发送的最左路由器b会比b左侧的路由器多收到一条来自i的信号; 它右侧发送不到的第一个路由器c会比c左侧的路由器少收到一条来自i的信号。 """ diff = [0]*(n + 1) for i in range(n): # 对于路由器i,用其作为左边界的次数-作为右边界的次数,就可以得到它比前一台路由多收到几条信号,即差分 diff[area[i][0]] += 1 diff[area[i][1]] -= 1 # 利用差分数组的前i项和就可以还原出原数组的第i项 count = 0 counter_i = 0 for i in range(n + 1): counter_i += diff[i] if counter_i >= k: count += 1 print(count)
#include <iostream> (720)#include <bits/stdc++.h> using namespace std; //学习到了查分数组,进行复杂度为o(n)的遍历 int coverage(vector<int> num,int k) { int n=num.size(); vector<int> dp(n,0); for(int i=0;i<n;i++) { dp[max(0,i-num[i])]+=1; if(i+num[i]+1<n) dp[i+num[i]+1]-=1; } int res=0; int cover=0; for(int i=0;i<n;i++) { cover+=dp[i]; if(cover>=k) res++; } return res; } int main() { int N,k; while(cin>>N>>k) { vector<int> strength(N,0); for(int i=0;i<N;i++) { cin>>strength[i]; } cout<<coverage(strength,k); } }
#include <bits/stdc++.h> using namespace std; int main(){ int n,k; cin>>n>>k; int a[n+1],c[n+1]; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++){ cin>>a[i]; c[max(0,i-a[i])]++; c[min(n+1,i+a[i]+1)]--; } int cnt=0,t=c[0]; for(int i=1;i<=n;i++){ t += c[i]; if(t>=k) cnt++; } cout<<cnt<<endl; return 0; }
package main import "fmt" func main() { var n, k, a int ans, count := 0, 0 fmt.Scan(&n) arr := make([]int, n+1) fmt.Scan(&k) for i := 0; i < n; i++ { fmt.Scan(&a) arr[max(0, i-a)]++ arr[min(n, i+a+1)]-- } for i := 0; i < n; i++ { count += arr[i] if count > k { ans++ } } fmt.Println(ans) } func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b }
import java.io.*; import java.util.Scanner; public class Main { public static void main(String[] args) throws IOException{ Scanner in = new Scanner(System.in); int n= in.nextInt(); int k = in.nextInt(); int []temp=new int[n+2]; int ans=0; for(int i=1;i<=n;i++){ int r=in.nextInt(); temp[Math.min(n+1,i+r+1)]-=1; temp[Math.max(0,i-r)]+=1; } int res=0; ans=temp[0]; for(int i=1;i<=n;i++){ ans+=temp[i]; if(ans>=k)res++; } System.out.println(res); } }
import java.util.*; public class Main { public static void main(String args[]) { Scanner sc = new Scanner(System.in); int n ,k; n = sc.nextInt(); k = sc.nextInt(); // 构造差分数组 int[] router = new int[n + 5]; int ans = 0; for(int i = 1; i <= n; i++) { int sig = sc.nextInt(); for(int j = sig; j >= 0; j--) { if(i - j >=1) { router[i-j] += 1; break; } } for(int j = sig; j >=0; j--) { if(i + j + 1 <= n + 2) { router[i+j+1] -= 1; break; } } } int init = 0; for(int i = 1 ; i <= n; i++) { init += router[i]; if(init >= k) ans += 1; //System.out.println(init); } System.out.println(ans); } }
//差分 #include<iostream> using namespace std; const int N = 1e5 + 5; int a[N]; int diff[N]; int n, k; int main() { cin >> n >> k; for (int i = 1; i <= n; ++i) { cin >> a[i]; } //i - j <= a[i] for (int i = 1; i <= n; i++) { diff[max(1, i - a[i])]++; diff[min(n + 1, i + a[i] + 1)]--; } int ans = 0; for (int i = 1; i <= n; ++i) { diff[i] += diff[i - 1]; if (diff[i] >= k) { ans++; } } cout << ans << endl; return 0; }
import java.util.*; public class Main { public static void main(String[] args) { Scanner scanner=new Scanner(System.in); int n=scanner.nextInt(); int k=scanner.nextInt(); int[] a=new int[n+1]; int[] countl=new int[n+1]; int[] countr=new int[n+1]; for(int i=1;i<=n;i++){ a[i]=scanner.nextInt(); } for(int i=2;i<=n;i++){ if(Math.abs(i-1)<=a[i]) {//1号收到来自右边的信号 countr[1]+=1; } } for(int i=2;i<=n;i++){ countr[i]=countr[i-1];//i接受右边到的信号数量应该等于i-1接收到的数量 if(a[i]>=1) countr[i]-=1;//如果i给i-1提供了信号,那么i右边信号量-1 } // for(int i=1;i<=n;i++) System.out.print(countl[i]); // System.out.println(); for(int i=n-1;i>=0;i--){ if(Math.abs(i-n)<=a[i]) {//n号收到来自左边的信号 countl[n]+=1; } } for(int i=n-1;i>=0;i--){ countl[i]=countl[i+1]; if(a[i]>=1) countl[i]-=1; } // for(int i=1;i<=n;i++) System.out.print(countr[i]); // System.out.println(); int ans=0; for(int i=1;i<=n;i++) if((countl[i]+countr[i]+1)>=k) ans++; System.out.println(ans); } }这样只通过了1/3,为什么呢?
#include<iostream> #include<vector> using namespace std; int main() { int n, k; cin >> n >> k; vector<int>distances(n); for(int i = 0; i < n; i++) { cin >> distances[i]; } vector<int>chafen_counts(n, 0); for(int i = 0; i < n; i++) { if(i - distances[i] > 0) { chafen_counts[i - distances[i]] += 1; } else { chafen_counts[0] += 1; } if(i + distances[i] + 1 <= n-1) { chafen_counts[i + distances[i] + 1] -= 1; } } for(int i = 0; i < n; i++) { if(i > 0) { chafen_counts[i] += chafen_counts[i - 1]; } } int number = 0; for(int i = 0; i < n; i++) { if(chafen_counts[i] >= k) { number++; } } cout << number; return 0; }
首先得明白差分数组的概念。
差分数组:对于数组[a1,a2,a3,a4...],其差分数组[a1,a2-a1,a3-a2,a4-a3...]。第i项的值等于查分数组前i项的和。
这个概念有什么用呢?加入我们需要在[i,j)范围内都进行+1操作,对于原数组,可以遍历第i到j项都进行+1,但这样时间复杂度有点大。
如果用了差分数组,就可以在第i项+1,第j+1项-1就行了。只需操作两个地方。
对于这题,某一个路由器ax,其辐射的范围从i到j,我们如果遍历每个x,再从对应的i到j都+1操作,时间复杂度太大。那么就可以用差分数组。
用一个数组dp[]表示第i个路由器能接受到多少个信号,都是从0开始,遍历路由器数组arr[],计算出每一个路由器辐射的范围,start到end,然后对差分数组进行dp[start]++,dp[end+1]--操作,即表示对结果数组从start到end进行+1操作.
首先得明白差分数组的概念。
差分数组:对于数组[a1,a2,a3,a4...],其查分数组[a1,a2-a1,a3-a2,a4-a3...]。第i项的值等于查分数组前i项的和。
这个概念有什么用呢?加入我们需要在[i,j)范围内都进行+1操作,对于原数组,可以遍历第i到j项都进行+1,但这样时间复杂度有点大。
如果用了差分数组,就可以在第i项+1,第j+1项-1就行了。只需操作两个地方。
对于这题,某一个路由器ax,其辐射的范围从i到j,我们如果遍历每个x,再从对应的i到j都+1操作,时间复杂度太大。那么就可以用差分数组。
用一个数组dp[]表示第i个路由器能接受到多少个信号,都是从0开始,遍历路由器数组arr[],计算出每一个路由器辐射的范围,start到end,然后对差分数组进行dp[start]++,dp[end+1]--操作,即表示对结果数组从start到end进行+1操作.
最后,累加遍历dp数组,就能得到每一个路由器能接收的到信号数.
// 对于路由器i,它能收到信号的路由器可以分为左侧的、自己、右侧的:counter[i] = 被左侧信号覆盖 + 1 + 被右侧信号覆盖 // 被左侧信号覆盖,可以用最小优先队列实现;同理,被右侧信号覆盖,可以用最大优先队列实现。 #include<iostream> #include<vector> #include<queue> using namespace std; int solution(vector<int> &A, int k) { if (A.size()<k || A.empty()) return 0; priority_queue<int, vector<int>, greater<int>> q_l; priority_queue<int> q_r; vector<int> counter(A.size()); for (int i=0; i<A.size(); ++i) { while (!q_l.empty() && q_l.top()<i) q_l.pop(); counter[i] += q_l.size(); q_l.push(i+A[i]); } for (int i=A.size()-1; i>=0; --i) { while (!q_r.empty() && q_r.top()>i) q_r.pop(); counter[i] += q_r.size(); q_r.push(i-A[i]); } int res=0; for (int i=0; i<A.size(); ++i) { if (counter[i]>=k-1) ++res; } return res; } int main(int argc, char **argv) { ios::sync_with_stdio(false); int n, k; if (cin >> n >> k) { vector<int> A(n); for (int i=0; i<n; ++i) cin >> A[i]; cout << solution(A, k) << endl; } return 0; }