--原題見洛谷P3954 - P3957
T1成績
一看就是 A * 0.2 + B * 0.3 + C * 0.5 .
復雜度O (1).
T2 圖書管理員
我們可以發現,輸入數據給定了需求碼的位數,所以我們可以預處理出10的1-9次冪,
然后在每次詢問中我們可以把圖數碼 % ( 10^k ) 其中k為需求碼的位數.
復雜度 O(nq).
T3棋盤
記憶化寬搜
記錄狀態:
x,y 表示當前坐標;
cost 表示當前的花費;
magic 表示上一步是否用了魔法,如果用了,magic標記為上一步把這個格子變成了什么顏色,否則magic標記為-1;(這個很關鍵!)
然后,寬搜時我們可以開一個二維數組ans[][]來維護每個格子的Ans,(初始化為INT_MAX)
最后我們輸出ans[m][m] 或 -1 作為答案。
//具體見文末代碼(有注釋)
T4跳房子
二分答案+DP
顯然這個問題單調,所以我們可以二分答案(不知道的自行百度)。
二分答案中我們可以DP作為check,
50分做法:
F[i] 表示 從 1 跳到 i 的最大分數
顯然 F[i] = MAX( F[j] ) + s_i, 其中 j 為所有可以跳到 i 的格子。
復雜度O(n^2*k),其中 k為二分答案的check次數
但顯然過不了(手動滑稽)。
100分做法:
在上一個算法的基礎上,用單調隊列/線段樹 優化DP到 O(nlogn)
復雜度O(n * logn * k)
T3代碼:
#include <bits/stdc++.h>
#define maxm 500
using namespace std;
inline int read(){ // 讀入優化(But沒用啊QWQ)
int x = 0,f = 1; char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1;c = getchar();}
while (c <='9' && c >='0') {x = x * 10 + c - '0';c = getchar();}
return x * f;
}
int c[maxm+10][maxm+10],n,m; // 記錄顏色和矩陣規模
int ans[maxm+10][maxm+10]; //ans數組
struct ZT{int x,y,magic,cost;}; // 狀態記錄,見上分析
queue <ZT> Q; // STL隊列
int tx[4] = {0,-1,0,1},ty[4] = {-1,0,1,0}; // 坐標增量
bool Try(int x,int y){ // 一個小判斷
return x<=m && y <= m && x>=1 && y >= 1;
}
int main(){
m = read(); n = read(); //讀入
for (int i = 1; i <= m; ++i)
for (int j = 1; j <= m; ++j)
c[i][j] = -1,ans[i][j] = -1;
for (int i = 1; i <= n; ++i){
int X = read(),Y = read(),C = read();
c[X][Y] = C; // 讀入顏色
}
ZT tmp; tmp.x = 1; tmp.y = 1; tmp.cost = 0; tmp.magic=-1; //第一個狀態
Q.push(tmp);
int Ans=-1;
while ( !Q.empty() ){ // 寬搜
tmp = Q.front();
if (tmp.x == m && tmp.y == m){ // 走到終點,更新Ans
if (Ans==-1) Ans = tmp.cost; else Ans = min(Ans,tmp.cost);
}
for (int i = 0; i <= 3; ++i){ // 四個方向
int nx = tmp.x+ tx[i],ny = tmp.y + ty[i];
if (Try(nx,ny)){ // 在棋盤中
if (c[nx][ny] != -1){ // 不用魔法
ZT New;
New.x = nx;New.y = ny; // 計算坐標
New.magic = -1; // 標記
New.cost = (c[tmp.x][tmp.y] == c[nx][ny]) ? tmp.cost : (tmp.cost+1); //計算花費
if (tmp.magic == c[nx][ny]) --New.cost; // 特判上一步有沒有用魔法
if (New.cost<ans[nx][ny] || ans[nx][ny] == -1)Q.push(New);
if (ans[nx][ny] == -1) ans[nx][ny] = New.cost;//如果可以更新ans,就更新
else ans[nx][ny] = min(ans[nx][ny],New.cost);
}
else if (tmp.magic == -1){ // 用魔法,條件:上一步magic標記為-1
ZT New;//同理可得
New.x = nx;New.y = ny; //坐標
New.magic = c[tmp.x][tmp.y]; //顏色標記
magic New.cost = tmp.cost+2; // 花費
if (New.cost<ans[nx][ny] || ans[nx][ny] == -1) Q.push(New);
if (ans[nx][ny] == -1) ans[nx][ny] = New.cost; // ans的更新和記錄
else ans[nx][ny] = min(ans[nx][ny],New.cost);
}//
}//
}//for循環結束
Q.pop();
}
printf("%d\n",Ans); // 輸出
return 0;
}
順便啰嗦幾句……
NOIP居然考掛了,只有250(說好的300分呢)(手動滑稽)
T1 T2 T3 本來都會,結果T3寫掛了(手動滑稽)
NOIP2017,再見!