21-題3 聖誕島的寶藏
【問題描述】
Angel走進一間門虛掩的房間,房間裏竟然滿是寶藏!,房間被分成N*N的格子, N <= 1000。Angel站在(1,1)的位置上。不過,Angel只能向下或向右走,再回到(1,1)點。問最少取多少次,能夠把所有的寶藏都取完?
【輸入格式】
第一行N。
以後N行,每行N個字符,“.”代表空格子,“*”代表寶藏。
【輸出格式】
一行,代表至少要取多少次,才能把寶藏取完。
【輸入樣例】
3
…
.*.
*..
【輸出樣例】
2
可以發現:對於一個點有寶藏的點x,y,其右上角都是不能取得。因爲如果能取得了其右上角[x+1,y-n]的寶藏,而Angel又只能向右或向下走,必然不可能我們再走到[x,y]。所以可以將x從小到大排序,然後在y上做最長上升序列。因爲如果上升的話就取不到n,而這些點不能一起取,而剩下的都可以在這些路徑基礎上走出來。
如果用O(N^2)的LIS可能會超時(極端情況下1000*1000全都是寶藏)。所以採用樹狀數組+LIS或者二分+LIS。時間複雜度爲O(nlogn)
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=1100;
char sr[N][N];
int k,num[N];
struct cc
{
int x,y;
}a[N*N];
inline bool cmp(const cc &a,const cc &b)
{
return a.x>b.x||a.x==b.x&&a.y>b.y;
}
int top,pos;
int main()
{
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
scanf("%s",sr[i]+1);
for(int j=1;j<=n;++j)
if(sr[i][j]=='*')
{
++top;
a[top].x=i;
a[top].y=j;
}
}
sort(a+1,a+1+top,cmp);
for(int i=1;i<=top;++i)
{
pos=lower_bound(num+1,num+k+1,a[i].y)-num;//num是數組指針
if(pos>k) ++k;
num[pos]=a[i].y;
}
//num即爲最長上升序列
printf("%d",k);
}
21-題2
第2題 矩形
【問題描述】
在一個空間座標系裏,擺放了許許多多矩形,而對於每一個矩形,它的每條邊都與某一條座標軸平行。
有些矩形之間是相互接觸的,而有些不是。給出兩個矩形,如果他們擁有任何公共的部分,那麼它們就被認爲是相互接觸。
現在給出一系列矩形,要你求,有多少對矩形相互接觸。
【輸入格式】
第一行包含一個整數N(1≤N≤2000),表示矩形數量。
接下來的N行,每行包含6個整數。前三個數是矩形某個頂點的座標值,後三個則是其對角的座標值。由於所有邊都平行於座標軸,我們總能確定下這個矩形每個頂點的位置。
所有的座標值都是正整數,且不超過1000。
【輸出格式】
輸出一個整數,表示有多少對矩形相互接觸。
【測試樣例】
prostor.in
3
1 1 1 1 3 3
1 3 3 1 6 6
1 4 4 1 5 5
prostor.out
2
prostor.in
3
15 10 10 15 20 20
10 15 10 20 15 20
10 10 15 20 20 15
prostor.out
3
prostor.in
5
4 4 5 4 3 2
5 3 2 4 3 1
5 4 3 1 1 3
1 4 3 1 5 4
5 5 4 5 4 2
prostor.out
4
首先聯想一下二維時候的情景。二維的時候,如何判斷兩個矩形是否重疊呢?可以通過判斷相應角大小來實現。比如:
拓展到三維的角度也應該是如此:
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=2100;
int a[N][7];
inline bool check(int x,int y)
{
for(int i=1;i<=3;++i)
if(a[x][i+3]<a[y][i]||a[y][i+3]<a[x][i]) return 0;
return 1;
}
int main()
{
freopen("prostor.in","r",stdin);
freopen("prostor.out","w",stdout);
n=read();
for(int i=1;i<=n;++i)
{
for(int j=1;j<=6;++j) a[i][j]=read();
for(int j=1;j<=3;++j)
if(a[i][j]>a[i][j+3]) swap(a[i][j],a[i][j+3]);//使a[i][j]<a[i][j+3]
}
int ans=0;
for(int i=1;i<=n;++i)
for(int j=i+1;j<=n;++j)
ans+=check(i,j);
cout<<ans;
}
題2 最聰明的機器人(robotB)
【背景】
Wind設計了很多機器人。但是它們都認爲自己是最強的,於是,一場比賽開始了~
【問題描述】
機器人們都想知道誰是最聰明的,於是它們進行如下一種遊戲。
這個遊戲由2次機器人進行,2個機器人分別報出一個數n1,n2,誰報得大,就以這個數作爲基數,並由它先開始,輪流進行如下操作:
選取一個不大於基數的素數或者1,從基數中扣掉它。誰把基數扣到0,誰就贏了。
爲了公平,他們會進行10次比賽,請你分別輸出這10次誰獲勝了。
【輸入格式】robotB.in
每組測試數據均有10行
每行2個數n1,n2 (n1,n2<=maxlongint n1<>n2)
【輸出格式】robotB.out
對每組測試數據輸出10行,每行一個整數1或2
表示哪個機器人能獲勝
【輸入樣例】
10 9
8 10
10 7
6 10
8 9
9 7
6 9
9 5
3 2
1 2
【輸出樣例】
1
2
1
2
2
1
2
1
1
2
【註釋hint】
聰明的機器人當然會採取最優策略
1.任何一個大於4的數都可以被分成4k,4k+1,4k+2,4k+3的形式。對於4k+1,下一輪可以讓其變爲4k。4k+2,4k+3也同樣可以變成4k。
2.任何一個大於2的質數都可以分解成4k+1或者4k-1的形式
3.如果先手者或者當前執行者的數是4的倍數,那麼他的對手可以再將他變成4的倍數或者0。
所以:摸到4的倍數的會輸,摸到非4的倍數則能贏。
題4 定向越野(adven)
【問題描述】
交大閔行校區位於上海西南處,離市中心距離略遠,因此佔地面積巨大,足有5000畝之多,校園周長達8公里,因而校團委學生會充分利用了資源,每年都要在校園裏舉辦定向越野比賽,但規則與普通定向越野不同,每個隊被要求從某個起點出發最後到達終點,只要是地圖上每個被標註的點都可以走,經過一個點時必須在打卡器上打卡作記錄,記錄該點的打卡器所在位置的海拔高度,高度用一個非負整數來量度,該數將會被所保存在卡中。最後到達終點時,該隊的成績就爲卡中記錄的最大數與最小數之差,差最小的隊伍將摘取桂冠。
ZZ和他的同學也參與了這項運動,拿到地圖後,他們想要迅速找到一條最佳路線以確保獲得冠軍。
PS:其實光腦子好能算出最佳路線還不夠,還得能跑,但我們假設ZZ他們隊個個都是SUPERMAN,只要你幫助他們找到了最佳路線,他們就能獲得冠軍。
【輸入格式】
數據的第一行包含一個正整數n,表示校園地圖上共有n*n個被標註的點(n≤100)
接下來n行每行有n個非負整數ai,j,表示該點的打卡器所在位置的高度。(ai,j≤200)
ZZ和他的同學從(1,1)出發,目的地爲(n,n)
【輸出格式】adven.in
文件包含一個整數,即最小的高度差的值
【輸入樣例】adven.out
5
1 1 3 6 8
1 2 2 5 5
4 4 0 3 3
8 0 2 2 4
4 3 0 3 1
【輸出樣例】
3
【樣例說明】
最佳路線爲(1,1)-- (1,2)-- (2,2)-- (2,3)-- (3,3)-- (4,3)-- (4,4)-- (5,4)-- (5,5)。路線上最高高度爲3,最低高度爲0,所以答案爲3。當然,最佳路線可能不止一條。
【數據規模】
對於40%的數據, 保證N≤20
對於100%的數據,保證N≤100
全圖的最低高度和最高高度是可以得到的。可以固定下最低點,通過二分法來枚舉最高點,並通過搜索,判斷最後能否走到[n,n]來確定該方案是否可行。
原題解:
初看此題,可能很多同學會想到使用動態規劃的方法去求解。然而,隨之而來的問題是
如何去表示狀態。這是比較難解決的問題。
不妨換個思路。對整個地圖,能否採用搜索的方法,去尋找一條道路,然後找出這條路
中每個點海拔的最高和最低值呢。純粹的搜索顯然會超時,轉變一下想法。假如我們知道最
高和最低值,然後去判斷從起點到終點是否存在這樣的一條路,若存在則縮小這個最高最低
的差值,並重復這樣的判斷,理論上是可行的。
接下來的問題便是如何來確定這最高和最低值了。再讀一下題目,每個點的海拔高度的
範圍爲:0-200。這樣的話就可以嘗試枚舉最高最低點的高度,進而判斷是否存在一條路徑。
在枚舉的同時也可以剪掉很多的比當前得到的最優解不優的高度點。在枚舉最高最低點的基礎上,我們可以採用這樣的方法:枚舉最低點,然後二分最高點,進行判斷。這樣的話可以將複雜度縮小一個常數,更優!
總結:
1. 要注意題中數據的範圍,如本題高度的範圍,可能會對解題有所幫助。
2. 當一個思路無法繼續的時侯,不妨換個思想。尤其是不要將簡單題目複雜化
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int N=110;
int a[N][N];
struct cc
{
int x,y;
}q[N*N];
const int dx[]={1,-1,0,0};
const int dy[]={0,0,1,-1};
int Min;
bool vis[N][N];
inline bool check(int Max)
{
if(a[1][1]>Max||a[1][1]<Min) return 0;
int t=0,w;
q[w=1].x=1;
q[w=1].y=1;
memset(vis,0,sizeof(vis));
vis[1][1]=1;
int e,ux,uy,vx,vy;
while(t<w)
{
++t;
ux=q[t].x;
uy=q[t].y;
for(e=0;e<=3;++e)
{
vx=ux+dx[e];
vy=uy+dy[e];
if(vx>=1&&vx<=n&&vx>=1&&vy<=n)
if(!vis[vx][vy]&&a[vx][vy]>=Min&&a[vx][vy]<=Max)
{
vis[vx][vy]=1;
++w;
q[w].x=vx;
q[w].y=vy;
}
}
}
return vis[n][n];
}
int ans;
int main()
{
freopen("adven.in","r",stdin);
freopen("adven.out","w",stdout);
n=read();
int e=0;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
a[i][j]=read();
e=max(e,a[i][j]);
}
int l,r,mid;
ans=1e9;
for(Min=0;Min<=e;++Min)
{
l=Min;
r=e;
while(l+1<r)
{
mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
if(check(r)) ans=min(ans,r-Min);
if(check(l)) ans=min(ans,l-Min);
}
cout<<ans;
}
8-22第4題 機器選擇
【題目描述】
自從省隊NOI賽前集訓在scz舉行之後,一個名叫cs1.6.exe的文件開始在機房廣泛使用起來。每天大家都要找神犇小X借移動硬盤,考裏面的這個文件。
由於機房裏需要考這個文件的人太多了,每天都要花一段時間一個人一個人的去拷貝。小T覺得這實在是太麻煩了,就想找一個一勞永逸的方法。
小T調查了一下,機房有n臺機器,且有局域網,所有機器通過一些網線連接起來,其整個佈局是一個樹形結構,即任意兩臺機器間都有且僅有一條路徑。小T想在其中某一臺機器上儲存這個文件,需要的同學就可以直接通過局域網來下載這個文件。
網絡上信息傳輸是需要時間的,我們定義兩臺機器間數據傳輸的時間爲連接這兩臺機器的路徑所包含的網線數量。雖然機房裏通過局域網傳個文件是很快的,但對於急不可耐的同學們來說,一分一秒都是寶貴的,文件傳輸越快越好。所以小T要選擇一臺機器存儲文件,使得所有機器下載這個文件需要的總時間(即最後一臺機器完成下載的時間)儘可能短。
現在,你需要給出這個最短時間,以便讓小T看看他的決策是否最優。【輸入數據】
從selc.in中讀入數據。
第1行:一個整數n。
第2~n行:兩個整數u、v,即u、v兩臺機器間有一條網線連接。機器從1~n編號。
輸入數據保證是一個連通的樹型結構。【輸出數據】
向selc.out中輸出數據。
1行一個整數,即最短的時間。【數據範圍】
對於30%的數據,n≤100;
對於50%的數據,n≤1000;
對於100%的數據,2≤n≤100000。【輸入輸出樣例】
selc.in selc.out
5
3 2
2 1
5 2
2 4 1
樹上的最長鏈稱爲樹的直徑。
用兩次dfs找出樹的直徑,然後除以2取整就行了。
第2題 棋盤
【問題描述】
在一個N×N的棋盤上,一些格子裏填了字母,並且在整個棋盤上,沒有哪個字母出現兩次或以上。在這些填了字母的格子中,有一些這樣的情況:三個格子的中心處在一條直線上。現在你的任務是,找出所有這樣三個一組的格子,滿足它們在一直線上。輸出有多少組即可。【輸入格式】
第一行包含一個整數N(3≤N≤100),表示棋盤尺寸。
下面N行,每行N個字符描述了整個棋盤。其中包只含大寫字母,空格則用點表示。【輸出格式】
輸出所有的三個一組的總數。【測試樣例】
trojke.in
4
…D
..C.
.B..
A…
trojke.out
4trojke.in
5
..T..
A….
.FE.R
….X
S….trojke.out
3
trojke.in
10
….AB….
..C….D..
.E……F.
…G..H…
I……..J
K……..L
…M..N…
.O……P.
..Q….R..
….ST….trojke.out
0
這題關鍵是隻有26個字母,因此可以枚舉每一個字母的位置斜率來判斷
時間複雜度爲O(n^3)
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
struct cc
{
int x,y;
}a[27];
int main()
{
freopen("trojke.in","r",stdin);
freopen("trojke.out","w",stdout);
int n,tot=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%*");
for(int j=1;j<=n;++j)
{
char ch;
scanf("%c",&ch);
int num=int(ch)-'A'+1;
if(num>=1&&num<=26)
{
++tot;
a[tot].x=i;
a[tot].y=j;
}
}
}
int ans=0;
for(int i=1;i<=tot;++i)
for(int j=i+1;j<=tot;++j)
for(int k=j+1;k<=tot;++k)
if((a[i].y-a[k].y)*(a[i].x-a[j].x)==(a[i].y-a[j].y)*(a[i].x-a[k].x))++ans;//斜率相同..
cout<<ans;
}
第3題 架設電線
【問題描述】
在JS大學參加數學夏令營的同學對宿舍供電的穩定性給予了極大的關注,因爲大家都住在7~16層,如果停電,嗚嗚~~~~
所以,他們要求負責人把那些老舊的電線換成性能更好的新電線。新的電線架設在已有的N 根電線杆上,第i根電線杆的高度爲Hi米。電線總是從一根電線杆的頂端被引到相鄰的那根的頂端,如果這兩根電線杆的高度不同,那麼負責人就必須爲此支付“C * 電線杆高度差”的費用。當然,他不能移動電線杆,只能按原有的順序在相鄰杆間架設電線。
負責人認爲,加高某些電線杆能減少架設電線的總花費,儘管這項工作也需要支出一定的費用。更準確地,如果他把一根電線杆加高X米的話,他得爲此付出X^2的費用。
負責人找到了小Y,提出這個“無理”要求的隊伍的首領(因爲他最怕爬樓了,所以他也最積極),要求小Y幫忙計算一下如果合理地進行這兩種工作,他最少要在這個電線改造工程上花多少錢。
不過,小Y不是有點懶而是相當懶,所以他把這個簡單的任務交給了在座的各位。【輸入格式】
第1行:兩個整數:N和C(2≤N≤100000,1≤C≤100),之間用一個空格隔開。
第2~N+1行:第i+1行僅有一個整數:Hi(1≤Hi≤100)。【輸出格式】
僅一1行一個整數,表示負責人完成電線改造工程所需要的最小花費。【輸入樣例】
5 2
2
3
5
1
4【輸出樣例】
15
【樣例說明】
一共有5根電線杆,在杆間拉電線的費用是每米高度差2元。在改造之前,電線杆的高度依次爲2,3,5,1,4米。
最好的改造方法是:負責人把第一根電線杆加高1米,把第四根加高2米,使得它們的高度依次爲3,3,5,3,4米。這樣花在加高電線杆上的錢是5元。此時,拉電線的費用爲2*(0+2+2+1) = 10,總花費爲15元。
狀態:設f[i][j]表示前i根電線杆,高度爲J時的最小花費。
轉移方程:f[i][j] = min(f[i-1][k]+abs(k-j)*c+(j-a[i])^2) (k表示前面那根電線杆的高度)
提出所有與k無關的量:
f[i][j] = min(f[i-1][k]+abs(k-j)*c)+(j-a[i])^2
然後去掉絕對值:
設P = min(f[i-1][k]+abs(k-j)*c),Q = (j-a[i]^2)
f[i][j] = P + Q
設A = min(f[i-1][k]+k*c)-j*c
B = min(f[i-1][k]-k*c)+j*c
記C[X][i] = min(f[i-1][k]+k*c)
D[X][i] = min(f[i-1][k]-k*c)
則C[i-1][j]+j*c = A
D[i-1][j+1]-j*c = B
由於只需要用到i-1行的值,所以可以用滾動數組。
高度只能增高,所以j,k>a[i]
時間複雜度爲O(N*100),空間複雜度爲O(2*100*3)
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
using namespace std;
int n,m,c,ans,Max;
inline int read()
{
char c;
int res,flag=0;
while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;
res=c-'0';
while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';
return flag?-res:res;
}
const int inf=1e9;
const int N=100001;
int now,past,h[N],f[2][102],C[2][102],D[2][102];
int Sqr(int x){return x*x;}
int main()
{
freopen("telewire.in","r",stdin);
// freopen("telewire.out","w",stdout);
memset(f,63,sizeof(f));
memset(C,63,sizeof(C));
memset(D,63,sizeof(D));
Max=-inf;
n=read();
c=read();
for(int i=1;i<=n;++i)
h[i]=read(),Max=max(Max,h[i]);
f[1][h[1]]=0;
for(int i=h[1];i<=Max;++i)
{
f[1][i]=f[1][h[1]]+Sqr(i-h[1]);
C[1][i]=min(C[1][i-1],f[1][i]-c*i);
}
for(int i=Max;i;--i)
D[1][i]=min(D[1][i+1],f[1][i]+c*i);
now=0;past=1;
//獲得第一根電線杆最小值
for(int i=2;i<=n;++i,swap(now,past))
//now,past即滾動數組
{
memset(f[now],63,sizeof(f[now]));
memset(C[now],63,sizeof(C[now]));
memset(D[now],63,sizeof(D[now]));
for(int j=h[i];j<=Max;++j)
{
f[now][j]=min(C[past][j]+c*j+Sqr(j-h[i]),D[past][j]-c*j+Sqr(j-h[i]));
C[now][j]=min(C[now][j-1],f[now][j]-c*j);
}
for(int j=Max;j;--j)
D[now][j]=min(D[now][j+1],f[now][j]+c*j);
}
ans=inf;
for(int i=h[n];i<=Max;++i) ans=min(ans,f[past][i]);
cout<<ans;
}
題2 蘋果旅遊(travel)
【背景】
xiaoT發現山谷相當的大,準確地說應該是相當的長,xiaoT想到山谷的那頭去看看,但是靠xiaoT走路的速度,到那邊要n年。還好xiaoT可以買一些蘋果,它把這些蘋果當成動力,根據火箭發射的原理,如果xiaoT把蘋果向後扔,xiaoT就會向前進。
【問題描述】
蘋果有兩種,一種青蘋果,一種紅蘋果。
已知到山谷的長度爲k,用一些(同一種類)蘋果可以通過的路程爲1。
蘋果的價格是不一樣的,紅蘋果的價格是紅蘋果個數的四次方。
青蘋果的價格就是青蘋果個數。
【輸入格式】travel.in
第一行有一個正整數n表示xiaoT走路到那邊需要的時間。
第二行有一個正整數k表示山谷的長度。
接下來k行,每行兩個正整數,分別表示通過該段:
如果使用紅蘋果,則需要的數量爲a
如果使用青蘋果,則需要的數量爲b
【輸出格式】travel.out
【樣例輸入】
2296
3
3 1000
2 5000
4 8000
【樣例輸出】
2296
【樣例解釋】
第1段用青蘋果,第2、3段用紅蘋果,花費是1000+(2+4)4 【數據規模】
對於30%的數據,k≤10
對於50%的數據,k≤25
對於100%的數據,k≤50
對於100%的數據,每段路消耗的紅蘋果的數量≤10
對於100%的數據,每段路消耗的青蘋果的數量≤107
一道DP。如果直接暴搜30,剪枝50。
狀態可以設f[i][j]表示前i段路,用了j個紅蘋果後所需的最小花費,也可以設做前i段路,用了j個紅蘋果後所需的最小青蘋果數。
這裏用第一種設法。
那麼狀態轉移方程爲:f[i][j] = min(f[i-1][j-red[i]]+(pow(j,4)-pow(j-red[i],4)),f[i-1][j]+qing[i]) (j>=a[i])
f[i][j] = f[i-1][j]+qing[i] (j>0&&j<a[i])
邊界條件:
f[0][i] = 0(不用走路不用花費)
f[k][i] = +INF
但注意INF要儘量大,數據類型也最好開到long long
由於狀態轉移方程只用了f數組第一維的2個,因此可以直接去掉,並倒序枚舉。
時間複雜度O(n^2),空間複雜度O(n)
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
int wtf,k;
long long red[52],qing[52];
long long f[501];
int min(long long a,long long b){
if(a<b) return a;else return b;
}
int main(){
freopen("travel.in","r",stdin);
freopen("travel.out","w",stdout);
scanf("%d",&wtf);
scanf("%d",&k);
if (k==0){
cout<<0;
return 0;
}
int cost = 0;
for (int i = 1;i<=k;i++){
scanf("%d%d",&red[i],&qing[i]);
cost+=red[i];
}
memset(f,0,sizeof(f));
//for (int i=0;i<=501;i++)f[0][i] = 0;
for (int i=1;i<=k;i++){
for (int j =cost;j>=0;j--){
if (j>=red[i])f[j]= min(f[j-red[i]]+pow(j,4)-pow(j-red[i],4),f[j]+qing[i]);
else f[j]=f[j]+qing[i];
}
}
long long ans = 0xfffffffffff;
for (int i =1;i<=cost;i++){
ans = min(ans,f[i]);
}
cout<<ans;
}
題4 最後的戰犯(maze)
【問題描述】
話說Lucky和Feli以3721部隊爲誘餌,殲滅了大批日軍。但頑固的日軍軍官小犬蠢一狼(以下簡稱小犬)在全軍覆滅之後仍然不肯認輸。他躲在山區的一個巖洞中,等待日軍的救援部隊。他妄圖憑藉複雜的地形躲避我軍的追蹤。於是,總部派出足智多謀的Feli前往巖洞搜尋小犬。
Feli來到巖洞入口,發現巖洞其實是一個巨大的迷宮。迷宮地形極爲複雜,爲一個正方形,其中佈滿了障礙物。迷宮可以分爲N*N(2≤N≤100)個區域,每個區域或者是空地,或者是不可逾越的障礙物。小犬就躲藏在其中某一個區域內。由於小犬已經忍受了幾天的飢餓,Feli進入迷宮時他已經失去思維處於迷亂狀態。小犬每秒鐘只會沿着他的方向直線前進,如果遇到障礙物或者迷宮邊界,他會立刻向右轉90度(不會花去時間),繼續沿直線前進(初始方向向北)。Feli每秒鐘可以自主決定往哪個方向走。如果同一時刻Feli與小犬位於同一個區域,或者相鄰的區域(非對角線相鄰),Feli可以立刻將小犬抓住。
Feli本來打算先確定小犬的位置,然後沿最短路線抓住他,但是Feli前進時小犬同時也在移動,就不能採取這種方法了。請你幫助Feli確定一種方案,使Feli抓獲小犬所用的時間最短。
【輸入格式】maze.in
輸入數據第一行是一個整數N。以下N行每行N個字符,“*”表示巖洞中的障礙物,“.”表示空地,“J”表示小犬(一開始他會向北走),“F”表示Feli。上北下南左西右東。
【輸出格式】maze.out
輸出數據僅一行,如果Feli能抓到小犬,那麼輸出所需的最短時間,如果Feli抓不到小犬,那麼這個最後的日本戰犯將在巖洞中餓死(因爲Feli將在離開的時候封閉巖洞的所有出口),此時輸出“No
【樣例輸入】
3
F*J
.*.
…
【樣例輸出】
3
這題我先用算出小犬走的10000步,然後在BFS Feil的路徑,判斷是否抓到。由於大概不可能有超過10000步的情況,我直接輸出No Solution.
但是這題的內存只有1M,所以我的方法一個點也過不了。如果直接用數據測的話或者開大內存可以過8個點。其中有一點不得不吐槽一下:
34
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
....*****..*...*...****..*...*...
....*.F....*...*..*......*.*.....
....*****..*...*..*......**......
....*......*...*..*......*.*.....
....*.......***....****..*...*...
.................................
.................................
.................................
.................................
.****...***...****....***...*...*
..J*...*...*..*...*..*...*..**..*
...*...*****..****...*****..*.*.*
*..*...*...*..*......*...*..*..**
.**....*...*..*......*...*..*...*
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
.................................
貼上我的代碼:
#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
const int xx[4] = {-1,0,1,0};
const int yy[4] = {0,1,0,-1};
struct position{
int x;
int y;
int t;
};
int n,fx,fy,jx,jy;
position doggo[10001];
int map[501][501];
void dogway(int nowx,int nowy,int pos,int time,int depth){
int tx = nowx+xx[pos];
int ty = nowy+yy[pos];
while(map[tx][ty]==true){
doggo[++time].x = tx;doggo[time].y = ty;depth--;
if (depth == 0)return;
tx = tx+xx[pos];
ty = ty+yy[pos];
}
nowx = tx-xx[pos];
nowy = ty-yy[pos];
int i = (pos+1)%4;
while(true){
tx = nowx+xx[i];
ty = nowy+yy[i];
if (map[tx][ty] == true){
dogway(nowx,nowy,i,time,depth);
break;
}
i = (i+1)%4;
}
}
bool check(int a,int b,int c){
if (doggo[c].x==a&&doggo[c].y==b) return true;
for (int i =0;i<4;i++){
int tx= a+xx[i];
int ty= b+yy[i];
if (doggo[c].x==tx&&doggo[c].y==ty){
return true;
}
}
return false;
}
int main(){
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d",&n);getchar();
memset(map,0,sizeof(map));
for (int i =1;i<=n;i++){
for (int j =1;j<=n;j++){
char ch;
scanf("%c",&ch);
if (ch == '*'){
map[i][j] = false;
}
if (ch == '.')map[i][j] = true;
if (ch == 'F'){
fx = i;fy= j;
map[i][j] = true;
}
if (ch == 'J'){
jx = i;jy = j;
map[i][j] = true;
}
}
getchar();
}
dogway(jx,jy,0,0,10000);
doggo[0].x = jx;doggo[0].y = jy;
queue<position>que;
position temp;
temp.x = fx;temp.y =fy;temp.t =0;
map[fx][fy] =false;
que.push(temp);
while(que.front().t<=10000){
int x = que.front().x;
int y = que.front().y;
int time = que.front().t;
que.pop();
if (check(x,y,time)){
cout<<time;
return 0;
}
else{
for (int i =0;i<4;i++){
int tx = x+xx[i];
int ty = y+yy[i];
if (map[tx][ty]== true){
map[tx][ty] = false;
temp.x = tx;temp.y =ty;temp.t =time+1;
que.push(temp);
}
}
}
}
cout<<"No solution.";
}
再貼一個1M內存下過了8個點的:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int l,r,x1,y1,x2,y2,x3,y3,xj,yj,ans,n,les[101][101];
bool tr[101][101],ma[101][101];
char a[102][102];
int dx[4]={0,1,-1,0};
int dy[4]={1,0,0,-1};
struct qw{
int xx;
int yy;
};
qw te[10001];
bool check(int x,int y)
{
if (x>0&&y>0&&x<=n&&y<=n)
if (!ma[x][y]) return true;
return false;
}
void dfs(int x,int y,int k,int di)
{
//printf("%d %d %d %d\n",x,y,k,di);
for (int i=0;i<=3;i++)
{
int x1=dx[i]+x;
int y1=dy[i]+y;
if (check(x1,y1)&&les[x1][y1]!=-1&&k>=les[x1][y1])
{
ans=k;
return;
}
}
if (les[x][y]!=-1&&k>=les[x][y])
{
ans=k;
return;
}
//if (k>10000) return;
//tim[k].xx=x;
//tim[k].yy=y;
int x0,y0;
if (di==1) x0=x-1,y0=y;
if (di==2) x0=x,y0=y+1;
if (di==3) x0=x+1,y0=y;
if (di==4) x0=x,y0=y-1;
while (!check(x0,y0))
{
di=di%4+1;
if (di==1) x0=x-1,y0=y;
if (di==2) x0=x,y0=y+1;
if (di==3) x0=x+1,y0=y;
if (di==4) x0=x,y0=y-1;
}
dfs(x0,y0,k+1,di);
}
int main()
{
freopen("maze.in","r",stdin);
freopen("maze.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%s",a[i]+1);
for (int j=1;j<=n;j++)
{
if (a[i][j]=='F')
{
x1=i;y1=j;
}
if (a[i][j]=='J')
{
xj=i;yj=j;
}
if (a[i][j]=='*') ma[i][j]=true;
}
}
//for (int i=1;i<=n;i++)
//for (int j=1;j<=n;j++) printf("%c",a[i][j]);
//dfs(xj,yj,0,1);
l=0;r=1;
te[1].xx=x1;
te[1].yy=y1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
les[i][j]=-1;
les[x1][y1]=0;
tr[x1][y1]=true;
while (l<r)
{
l++;
int x3=te[l].xx;
int y3=te[l].yy;
for (int i=0;i<=3;i++)
{
int x2=te[l].xx+dx[i];
int y2=te[l].yy+dy[i];
if (check(x2,y2)&&!tr[x2][y2])
{
r++;
te[r].xx=x2;
te[r].yy=y2;
les[x2][y2]=les[x3][y3]+1;
tr[x2][y2]=true;
}
}
}
/*for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
printf("%d ",les[i][j]);
printf("\n");
}*/
if (les[xj][yj]==-1) printf("No solution.");
else
{
dfs(xj,yj,0,1);
printf("%d",ans);
}
fclose(stdin);
fclose(stdout);
}
最後附上題解:
先對feli作BFS,找出feli到每個地方的最短距離,然後模擬japan行動判斷什麼時候兩人相遇。相遇的時間,就是花去的最短時間。當然我們得道個歉,這題內存是緊了點。不過既然是模擬賽。緊點還是有好處的吧。
Make it Manhattan
【題目描述】
混亂的城市已經變得無法控制。大樓隨處亂造,城市的佈局也是一片混亂。市長決定要結束這種局面,兵器並且想建造一個漂亮的、有組織的城市。
通過一些研究,他找到了使這個成爲現實的理想的方法。受到紐約曼哈頓的啓發,他希望所有的房子都造在矩形的格子裏,用南北向的林蔭道和東西向的街道隔開。這些林蔭道和街道都有相同的間距。在現在這種狀況,房子已經規劃在矩形格子裏面了。事實上,每幢房子都恰好完全覆蓋住一個矩形格子。但是,所有的樓房隨機地散落在城市裏,不毀壞房屋來造路是不可能的。爲了使得更多的市民開心,市長想要毀壞儘量少的房子。給你當前的房屋建造狀況,最小破壞量是多少呢?
上圖說明了這個問題。陰影方塊是原先的樓房。如果道路間距爲3,上面的粗線條就是規劃的道路,被經過的那個樓房就要被拆除了。 【輸入】
輸入文件的第一行爲兩個整數D和N,用一個空格隔開,分別代表道路間距、城市中的樓房數,1 <= D <= 1000, 1 <= N <= 100000。
以下N行,每行兩個整數x和y,用一個空格隔開,代表房屋的位置。-109 <= x,y <= 109。 【輸出】
輸出文件只有一行一個整數,代表最少需要毀壞的房屋數。 【樣例輸入】(對應上面的圖)
3 10
1 0
2 0
3 0
4 0
1 2
0 3
1 5
3 5
4 2
-2 2 【樣例輸出】
1
算法:
最長上升序列(nlogn):
for(int i=1;i<=top;++i)
{
pos=lower_bound(num+1,num+k+1,a[i].y)-num;//num是數組指針
if(pos>k) ++k;
num[pos]=a[i].y;
}
Treap樹