算法競賽進階指南0x 前綴和與差分

激光炸彈

一種新型的激光炸彈,可以摧毀一個邊長爲 R 的正方形內的所有的目標。

現在地圖上有 N 個目標,用整數Xi,Yi表示目標在地圖上的位置,每個目標都有一個價值Wi。

激光炸彈的投放是通過衛星定位的,但其有一個缺點,就是其爆炸範圍,即那個邊長爲 R 的正方形的邊必須和x,y軸平行。

若目標位於爆破正方形的邊上,該目標不會被摧毀。

求一顆炸彈最多能炸掉地圖上總價值爲多少的目標。

輸入格式
第一行輸入正整數 N 和 R ,分別代表地圖上的目標數目和正方形的邊長,數據用空格隔開。

接下來N行,每行輸入一組數據,每組數據包括三個整數Xi,Yi,Wi,分別代表目標的x座標,y座標和價值,數據用空格隔開。

輸出格式
輸出一個正整數,代表一顆炸彈最多能炸掉地圖上目標的總價值數目。

數據範圍
0<N≤10000,
0≤Xi,Yi≤5000
輸入樣例:
2 1
0 0 1
1 1 1
輸出樣例:
1

x和y的數據範圍都較小,暴力求解前綴和就好了,求前綴和習慣以1下標爲開頭,我們要做一個處理。

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N=5010;

int g[N][N];
int n,r;

int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    
    scanf("%d%d",&n,&r);
    int mx=r;
    int my=r;
    while(n--)
    {
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        x++;
        y++;
        mx=max(mx,x);
        my=max(my,y);
        g[x][y]+=w;
    }

    for(int i=1;i<=mx;i++)
        for(int j=1;j<=my;j++)
            g[i][j]=g[i][j]+g[i-1][j]+g[i][j-1]-g[i-1][j-1];
    
    int res=0;

    for(int i=r;i<=mx;i++)
        for(int j=r;j<=my;j++)
            res=max(res,g[i][j]-g[i-r][j]-g[i][j-r]+g[i-r][j-r]);
    
    cout<<res<<endl;

    return 0;
}

IncDec序列

給定一個長度爲 n 的數列 a1,a2,…,an,每次可以選擇一個區間 [l,r],使下標在這個區間內的數都加一或者都減一。

求至少需要多少次操作才能使數列中的所有數都一樣,並求出在保證最少次數的前提下,最終得到的數列可能有多少種。

輸入格式
第一行輸入正整數n。

接下來n行,每行輸入一個整數,第i+1行的整數代表ai。

輸出格式
第一行輸出最少操作次數。

第二行輸出最終能得到多少種結果。

數據範圍
0<n≤105,
0≤ai<2147483648
輸入樣例:
4
1
1
2
2
輸出樣例:
1
2

做法貪心+差分。我們對一段區間做加法或減法就是對差分數組的兩端做加法或者減法。貪心策略:我們要保證最終差分數組中都爲0,我們可以統計一下差分數組中的正數和負數。儘量把整數和負數結合,然後不夠的和前端點或者和後端點結合。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>

#define ll long long

using namespace std;

const int N=100010;

ll d[N];
int n;
int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&d[i]);
    ll cp=0;
    ll cn=0;
    for(int i=n;i>=2;i--)
    {
        d[i]-=d[i-1];
        if(d[i]<0) cn-=d[i];
        else if(d[i]>0) cp+=d[i];
    }
    printf("%lld\n",max(cn,cp));
    printf("%lld\n",abs(cn-cp)+1);

    return 0;
}

最高的牛

有 N 頭牛站成一行,被編隊爲1、2、3…N,每頭牛的身高都爲整數。

當且僅當兩頭牛中間的牛身高都比它們矮時,兩頭牛方可看到對方。

現在,我們只知道其中最高的牛是第 P 頭,它的身高是 H ,剩餘牛的身高未知。

但是,我們還知道這羣牛之中存在着 M 對關係,每對關係都指明瞭某兩頭牛 A 和 B 可以相互看見。

求每頭牛的身高的最大可能值是多少。

輸入格式
第一行輸入整數N,P,H,M,數據用空格隔開。

接下來M行,每行輸出兩個整數 A 和 B ,代表牛 A 和牛 B 可以相互看見,數據用空格隔開。

輸出格式
一共輸出 N 行數據,每行輸出一個整數。

第 i 行輸出的整數代表第 i 頭牛可能的最大身高。

數據範圍
1≤N≤10000,
1≤H≤1000000,
1≤A,B≤10000,
0≤M≤10000
輸入樣例:
9 3 5 5
1 3
5 3
4 3
3 7
9 8
輸出樣例:
5
4
5
3
4
4
5
5
5
注意:
此題中給出的關係對可能存在重複

這題我們也時利用貪心+差分做就可以了,還有就是用set記錄一下,防止出現重複值。如果兩頭牛能相互看見,那他們之間的牛一定是要比他們矮的,爲了保證總體身高最高,所有中間的牛比他們矮1。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>

#define pii pair<int,int>

using namespace std;

const int N=100010;

set<pii> s;
int d[N];
int n,p,h,m;

int main(){
    //freopen("data.in","r",stdin);
    //freopen("data.out","w",stdout);
    scanf("%d%d%d%d",&n,&p,&h,&m);
    d[1]=h;
    while(m--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a>b) swap(a,b);
        if(s.count({a,b})==0)
        {
            s.insert({a,b});
            d[a+1]-=1;
            d[b]+=1;
        }
    }
    for(int i=1;i<=n;i++)
    {
        d[i]+=d[i-1];
        printf("%d\n",d[i]);
    }

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章