1. 程式人生 > 其它 >2021-01-24更

2021-01-24更

技術標籤:演算法c++

差分與字首和

差分

T1 堆疊草堆

題目:

Bessie對她自己最近在農場周圍的惡作劇感到抱歉,於是她同意幫助Farmer John堆疊新送達的一批乾草捆。
開始時有N(N是奇數)個空草堆,標記為1…N。FJ將會給Bessie包含K條指令的序列,每條指令的格式為"A B",表示Bessie應該在草堆A…B中每一個草堆頂部新放一捆乾草。例如,如果Bessie聽到指令"10 13",那麼她應該在草堆10,11,12和13上各新放一捆乾草。
在Bessie完成了FJ的所有指令後,FJ想要知道N個草堆高度的中位數——也就是說,所有草堆排序後中央草堆(由於N是奇數,這個草堆是唯一的)的高度。請幫助Bessie確定FJ問題的答案。

輸入
第1行:兩個被空格分隔的整數N和K。
第2…1+K行:每行包含一條FJ的指令,其格式為兩個被空格分隔的整數A和B。
輸出
一個整數, Bessie 完成所有指令後草堆高度的中位數。
輸入樣例
7 4
5 5
2 4
4 6
3 5
輸出樣例
1

思路:

看到這個題目後,我第一時間是想到用迴圈把每次輸入的草堆挨個加1,但後來發現TLE了,就想起了用差分,在每次要加的第一個草堆處加1,再最後一個的後面一個-1,最後統一加回去,十分快速簡便。

程式碼如下:

#include<bits/stdc++.h> 
using namespace std;
int n,q,h,a[1000010],b[
1000010]; void insert(int l,int r) { b[l]+=1; b[r+1]-=1; } //差分函式 int main() { ios::sync_with_stdio(false);//加快輸入輸出 cin>>n>>q; for(int i=1;i<=q;i++) { int l,r; cin>>l>>r; insert(l,r);//對l~r的區間草堆加1 } for(int i=1;i<=n;i++) b[i]+=b[i-1];//字首和 sort(b+1,b+1+n); cout<<
b[n/2+1]; return 0; }

T2 套圈遊戲

題目:

小b在逛公園的時候發現了一個好玩的遊戲,有一個 n*n 的方格,每個方格里面有若干硬幣,你只要交完錢,老闆就給你 m 個方形的圈,你扔出去的圈會覆蓋部分方格,這些方格你可以從中獲得一枚硬幣,但是老闆不會做虧本生意的,他規定:如果一個方格被覆蓋2次或以上,則不能獲得這個方格中的硬幣。

現在已知小b扔出去的 m 個圈的左上角以及右下角座標,請計算出小b最終獲得多少枚硬幣。
輸入
第一行,兩個整數n和m。(2<=n<=2000)
接下來m行,每行4個數,分別表示圈的左上角座標以及右下角座標。(座標值均在1~n之間)
輸出
一個整數,表示獲得的硬幣數。
輸入樣例
5 3
2 2 3 3
3 3 5 5
1 2 1 4
輸出樣例
14

思路:

這道題目很明顯要用二維差分來做,二維差分其實和二維字首和(見T4)類似,能快速的實現多個數據的改變。

程式碼如下:

#include<bits/stdc++.h> 
using namespace std;
int n,m,q,h,b[2010][2010];

void insert(int x1,int y1,int x2,int y2)
{
	b[x1][y1]+=1;
	b[x2+1][y1]-=1;
	b[x1][y2+1]-=1;
	b[x2+1][y2+1]+=1;
}    //二維差分函式
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>q;
	while(q--)
	{
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		insert(x1,y1,x2,y2);//對(x1,y1)~(x2,y2)範圍內的圈子套中次數加1
	}				
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];//字首和
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(b[i][j]==1)
				h++;	 
	cout<<h;			
	return 0;
}

字首和

T3 好奇的金魚(二)

題目:

有n條金魚排成一隊,現在已知每條魚的體重w。

好奇的金魚會有m次詢問,每次詢問給出兩個值(l,r)
(r >= l),想計算出[l,r]之間的金魚體重之和。

輸入
第一行2個數,n和m,中間用空格分隔
(1 <= n, m <= 100000)。
之後n+m行,
第 1 至 n 行:每行一個數字w(0 <= w <= 1000)
第 n + 1 至 n + m 行:每行2個數字l,r,中間用空格分隔
(0 < l <= r <= n)
輸出
輸出共m行,每行一個數,對應詢問區間的金魚體重之和。
輸入樣例
3 3
1
3
5
1 2
1 3
2 3
輸出樣例
4
9
8

思路:

其實我第一次做這道題的時候還是懵B一個,上來就直接迴圈m次找答案,然後…就TLE了,後來我認真學習,知道了字首和這種方法,才A了這題。
字首和:一種找l~r區間的和的方法,很快。開始每次都把從a1到當前的和找到(s[n]=s[n-1]+a[n]),求l到r區間的時候直接把s[r]-s[l-1] (不然會減掉l)就可以得到了。

程式碼如下:

#include<bits/stdc++.h> 
using namespace std;
int a[100010],s[100010],n,m,h,d,f;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		s[i]=a[i]+s[i-1]; //字首和
	}
	for(int i=1;i<=m;i++)
	{
		cin>>d>>f;
		h=s[f]-s[d-1];
		cout<<h<<endl;
	}   
	return 0;
}

T4 子矩陣求和

題目:

給出一個n * m的矩陣a,矩陣元素a[i][j]小於1000,進行q次查詢,每次查詢給出子矩陣的4個邊界(上下左右),求該子矩陣所有元素之和。

比如有一個查詢:1 2 1 3 ,表示要計算從第1行到第2行,從第1列到第3列的所有元素之和。

在這裡插入圖片描述
輸入
第一行2個整數n, m,中間用空格分割,分別對應陣列的行數n、列數m(1 <= m,n <= 1000)

接下來n行,每行m個整數表示矩陣的內容a[i][j] 。
(0 <= a[i,j] <= 1000)

接下來1行,一個數q,對應查詢的數量。(1 <= q <= 10000)

接下來q行,每行4個整數,對應子矩陣的上下左右邊界u,d,l,r。(1 <= u <= d <= n, 1 <= l <= r <= m)
輸出
共q行,對應q個詢問的求和結果。

輸入樣例
3 4
1 2 3 4
5 6 7 8
9 10 11 12
3
1 3 1 2
1 2 1 3
1 3 1 3
輸出樣例
33
24
54

思路:

第一次看見這道題的時候,我剛學二維陣列,強行用暴力解法(for迴圈)解,直接TLE,然後想起字首和是個好東西,就用一維字首和做了一下,完美的TLE了,想了半天,受到一維字首和的啟發,寫出了二維字首和,
順利AC

程式碼如下:

#include<bits/stdc++.h> 
using namespace std;
int a[1010][1010],n,m,gg,u,d,l,r,s[1010][1010],h;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>a[i][j];
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
		}
	}
	cin>>gg;
	for(int i=1;i<=gg;i++)
	{
		cin>>u>>d>>l>>r;
		cout<<s[d][r]-s[u-1][r]-s[d][l-1]+s[u-1][l-1]<<" ";
//二維字首和
	}
	return 0;
}