(一)雙指針css
1.日誌統計ios
小明維護着一個程序員論壇。如今他收集了一份」點贊」日誌,日誌共有 N 行。nginx
其中每一行的格式是:程序員
ts id
表示在 ts 時刻編號 id 的帖子收到一個」贊」。編程
如今小明想統計有哪些帖子曾經是」熱帖」。數組
若是一個帖子曾在任意一個長度爲 D 的時間段內收到很多於 K 個贊,小明就認爲這個帖子曾是」熱帖」。bash
具體來講,若是存在某個時刻 T 知足該帖在 [T,T+D) 這段時間內(注意是左閉右開區間)收到很多於 K個贊,該帖就曾是」熱帖」。oop
給定日誌,請你幫助小明統計出全部曾是」熱帖」的帖子編號。ui
輸入格式
第一行包含三個整數 N,D,K
spa
如下 N 行每行一條日誌,包含兩個整數 ts 和 id。
輸出格式
按從小到大的順序輸出熱帖 id。
每一個 id 佔一行。
數據範圍
1≤K≤N≤105,
0≤ts,id≤105,
1≤D≤10000
輸入樣例:
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3
輸出樣例:
1
3
解題思路:排序+雙指針
①對全部的贊按時間排序
②經過雙指針i,j維護長度不大於d的區間,並記錄該帖子中的獲贊數
#include<iostream>
#include<algorithm>
#include<cstdio>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N=100010;
int n,d,k;
int cnt[N];
bool ts[N];
PII flags[N];
int main()
{
int i,j;
scanf("%d%d%d",&n,&d,&k);
for(i=0;i<n;i++)
scanf("%d%d",&flags[i].x,&flags[i].y);
sort(flags,flags+n);
for(i=0,j=0;i<n;i++)
{
int id=flags[i].y;
cnt[id]++;
while(flags[i].x-flags[j].x>=d)
{
cnt[flags[j].y]--;
j++;
}
if(cnt[id]>=k)
ts[id]=true;
}
for(i=0;i<=100000;i++)
{
if(ts[i])
printf("%d\n",i);
}
return 0;
}
(二)BFS
1.獻給阿爾吉儂的花束
阿爾吉儂是一隻聰明又慵懶的小白鼠,它最擅長的就是走各類各樣的迷宮。
今天它要挑戰一個很是大的迷宮,研究員們爲了鼓勵阿爾吉儂儘快到達終點,就在終點放了一塊阿爾吉儂最喜歡的奶酪。
如今研究員們想知道,若是阿爾吉儂足夠聰明,它最少須要多少時間就能吃到奶酪。
迷宮用一個 R×C 的字符矩陣來表示。
字符 S 表示阿爾吉儂所在的位置,字符 E 表示奶酪所在的位置,字符 # 表示牆壁,字符 . 表示能夠通行。
阿爾吉儂在 1 個單位時間內能夠從當前的位置走到它上下左右四個方向上的任意一個位置,但不能走出地圖邊界。
輸入格式
第一行是一個正整數 T,表示一共有 T 組數據。
每一組數據的第一行包含了兩個用空格分開的正整數 R 和 C,表示地圖是一個 R×C 的矩陣。
接下來的 R 行描述了地圖的具體內容,每一行包含了 C 個字符。字符含義如題目描述中所述。保證有且僅有一個 S 和 E。
輸出格式
對於每一組數據,輸出阿爾吉儂吃到奶酪的最少單位時間。
若阿爾吉儂沒法吃到奶酪,則輸出「oop!」(只輸出引號裏面的內容,不輸出引號)。
每組數據的輸出結果佔一行。
數據範圍
1<T≤10,
2≤R,C≤200
輸入樣例:
3
3 4
.S.. ###. ..E. 3 4 .S.. .E.. .... 3 4 .S.. #### ..E.
輸出樣例:
5
1
oop!
解題思路:要求最短的距離,應該用BFS來進行求解,咱們須要map數組來存儲地圖,同時定義一個pair數組來存儲當前或者下一步的位置,
在定義一個vis數組來記錄走的步數及是否走過該條路,每進行一次bfs都要對vis數組進行初始化。
代碼:
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#define x first
#define y second
using namespace std;
typedef pair<int,int> PII;
const int N=210;
int x[]={1,-1,0,0};
int y[]={0,0,1,-1};
int t,r,c;
char map[N][N];
int vis[N][N];
bool check(int X,int Y)
{
if(X<0||X>=r||Y<0||Y>=c)
return false;
if(map[X][Y]=='#')
return false;
if(vis[X][Y]!=0)
return false;
return true;
}
int bfs(int bx,int by)
{
queue<PII> q;
memset(vis,0,sizeof(vis));
vis[bx][by]=0;
PII m;
m.x=bx,m.y=by;
q.push(m);
while(q.size())
{
PII tem=q.front();
if(map[tem.x][tem.y]=='E')
return vis[tem.x][tem.y];
q.pop();
for(int i=0;i<4;i++)
{
int X=tem.x+x[i];
int Y=tem.y+y[i];
if(check(X,Y)==false)
continue;
vis[X][Y]=vis[tem.x][tem.y]+1;
PII tem2;
tem2.x=X,tem2.y=Y;
q.push(tem2);
}
}
return 0;
}
int main()
{
int i,j,bx,by;
cin>>t;
while(t--)
{
cin>>r>>c;
for(i=0;i<r;i++)
{
for(j=0;j<c;j++)
{
cin>>map[i][j];
if(map[i][j]=='S')
{
bx=i;
by=j;
}
}
}
int k=bfs(bx,by);
if(k)
{
cout<<k<<endl;
}
else
cout<<"oop!"<<endl;
}
return 0;
}
2.紅與黑
有一間長方形的房子,地上鋪了紅色、黑色兩種顏色的正方形瓷磚。
你站在其中一塊黑色的瓷磚上,只能向相鄰(上下左右四個方向)的黑色瓷磚移動。
請寫一個程序,計算你總共可以到達多少塊黑色的瓷磚。
輸入格式
輸入包括多個數據集合。
每一個數據集合的第一行是兩個整數 W 和 H,分別表示 x 方向和 y 方向瓷磚的數量。
在接下來的 H 行中,每行包括 W 個字符。每一個字符表示一塊瓷磚的顏色,規則以下
1)‘.’:黑色的瓷磚;
2)‘#’:白色的瓷磚;
3)‘@’:黑色的瓷磚,而且你站在這塊瓷磚上。該字符在每一個數據集合中惟一出現一次。
當在一行中讀入的是兩個零時,表示輸入結束。
輸出格式
對每一個數據集合,分別輸出一行,顯示你從初始位置出發能到達的瓷磚數(記數時包括初始位置的瓷磚)。
數據範圍
1≤W,H≤20
輸入樣例:
6 9
....
輸出樣例:
45
解題思路:一道bfs題,由起點開始向四周擴散,加上約束標準,和訪問數組vis,統計總共的個數,須要注意H是行,W是列,此外起點也算一個
所以一開始ans=1;
代碼:
#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
#define x first
#define y second
using namespace std;
int x[]={1,-1,0,0};
int y[]={0,0,1,-1};
const int N=30;
int w,h;
int vis[N][N];
char map[N][N];
typedef pair<int,int> PII;
bool check(int X,int Y)
{
if(X<0||X>=h||Y<0||Y>=w)
return false;
if(map[X][Y]=='#')
return false;
if(vis[X][Y]!=0)
return false;
return true;
}
int bfs(PII start)
{
int ans=1;
queue<PII> q;
memset(vis,0,sizeof(vis));
vis[start.x][start.y]=1;
q.push(start);
while(!q.empty())
{
PII beg;
beg=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int X=beg.x+x[i];
int Y=beg.y+y[i];
if(check(X,Y)==false)
continue;
ans++;
PII tem;
tem.x=X,tem.y=Y;
vis[X][Y]=1;
q.push(tem);
}
}
return ans;
}
int main()
{
int i,j;
PII start;
while(1)
{
scanf("%d%d",&w,&h);
if(w==0&&h==0)
break;
for(i=0;i<h;i++)
{
for(j=0;j<w;j++)
{
cin>>map[i][j];
if(map[i][j]=='@')
{
start.x=i,start.y=j;
}
}
}
cout<<bfs(start)<<endl;
}
return 0;
}
(三)圖論
1.交換瓶子
有 N 個瓶子,編號 1∼N,放在架子上。
好比有 5 個瓶子:
2 1 3 5 4
要求每次拿起 2個瓶子,交換它們的位置。
通過若干次後,使得瓶子的序號爲:
1 2 3 4 5
對於這麼簡單的狀況,顯然,至少須要交換 2 次就能夠復位。
若是瓶子更多呢?你能夠經過編程來解決。
輸入格式
第一行包含一個整數 N,表示瓶子數量。
第二行包含 N 個整數,表示瓶子目前的排列情況。
輸出格式
輸出一個正整數,表示至少交換多少次,才能完成排序。
數據範圍
1≤N≤10000,
輸入樣例1:
5
3 1 2 5 4
輸出樣例1:
3
輸入樣例2:
5
5 4 3 2 1
輸出樣例2:
2
解題思路:咱們的目的是讓交換的次數最少,看下圖

根據數字和對應的下標,將該數字與應該在的下標的數字相連,構成一個環,咱們的目的是讓每一個數字自成一個環。
當咱們進行同環內的兩個數交換時:裂成兩個環
當進行不是同環內的交換時:會將兩個環合併成一個環
假設初始的環個數爲k,咱們要將他變成n個自環,咱們須要至少n-k步操做
所以,對於本題的解法就是找出初始的環數
代碼:
#include<iostream>
using namespace std;
const int N=10010;
int a[N];
bool tr[N];
int main()
{
int i,j,n;
int ans=0;
cin>>n;
for(i=1;i<=n;i++)
cin>>a[i];
for(i=1;i<=n;i++)
{
if(!tr[i])
{
ans++;
for(j=i;!tr[j];j=a[j])
tr[j]=true;
}
}
cout<<n-ans<<endl;
return 0;
}