link
D Vanya and Treasure
題意:
n*m
的格子,一開始再(1,1)的位置,每個位置都有一個寶箱,每個寶箱都有一個編號,編號爲x的寶箱可以被編號爲x-1的寶箱裏的鑰匙打開,1號寶箱沒有鎖,問打開p號寶箱最少走多少步
思路:
很顯然, 由於 x 只能打開 x+1 的, 所以我們很容易的就想到了暴力 dp, 但是如果 x 的個數, x + 1 的個數乘起來很大的話, 那麼我們就會很浪費時間, 所以結果就是
- 乘積 小於 n*m 那麼我們就直接暴力求結果,
- 乘積大於 n*m, 那麼我們就跑一遍 bfs, 一開始把所有 x 寶箱的位置加入到隊列, 然後跑完bfs 之後,我們只更新 x + 1 的答案。
反思:
我一開始只想到了暴力 dp 的方法, 結果顯而易見的就 T 了, 一時半會兒也想不到解決的辦法,
然後纔去看別人的博客,看到了小數據暴力, 大數據bfs 的方法,
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+1000;
const int M = 400;
const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
typedef pair<int,int> P;
vector<P>f[N];
int n,m,p,x,k;
bool vis[M][M];
int dis[M][M],ans[M][M],Ans;
void solve1(int now){
if (now == 0) ans[1][1] = 0;
for (auto itx:f[now]){
for (auto ity: f[now+1]){
ans[ity.first][ity.second] = min(ans[ity.first][ity.second], ans[itx.first][itx.second] + abs(ity.first - itx.first) + abs(itx.second - ity.second));
}
}
for (auto it: f[now+1]){
if (now + 1 == p) Ans = min(Ans, ans[it.first][it.second]);
}
if (now == 0 && k != 1) ans[1][1] = ans[0][0];
}
queue<P>q;
void solve2(int now){
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
dis[i][j] = ans[0][0],vis[i][j] = 0;
if (now == 0) dis[1][1] = 0;
else {
for (auto it: f[now])
dis[it.first][it.second] = ans[it.first][it.second];
}
while(!q.empty()) q.pop();
for (auto it: f[now]){
q.push(P{it.first, it.second});
vis[it.first][it.second] = 1;
}
while(!q.empty()){
P u = q.front(); q.pop();
for (int i = 0; i < 4; ++i){
int x = u.first + dx[i], y = u.second + dy[i];
if (x > 0 && x <= n && y > 0 && y <= m){
if (dis[x][y] > dis[u.first][u.second] + 1){
dis[x][y] = dis[u.first][u.second] + 1;
if (vis[x][y] == 0){
q.push(P{x,y});
vis[x][y] = 1;
}
}
}
}
vis[u.first][u.second] = 0;
}
for (auto it: f[now+1]){
ans[it.first][it.second] = dis[it.first][it.second];
}
}
int main(){
memset(ans, 0x3f3f, sizeof ans);
Ans = ans[0][0];
scanf("%d%d%d",&n,&m,&p);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j){
scanf("%d",&x);
f[x].push_back(P{i,j});
if (i == j && i == 1) k = x;
}
f[0].push_back(P{1,1});
ans[1][1] = 0;
for (int i = 0; i < p; ++i){
if ((int)f[i].size() * (int)f[i+1].size() <= n*m) solve1(i); else solve2(i);
// logs(i);
}
printf("%d\n",Ans);
return 0;
}
E. Vanya and Balloons
題意:
n*n的格子,每個格子都有一個數,現在可以在一個格子上放炸彈,炸彈炸+ x 形狀,得到的價值就是形狀裏面的值的乘積,問乘積最大是多少。炸彈炸了之後, 四個方向的長度要是一樣的。
思路:
問乘積最大, 由此可見, 乘積的路上不可以有 0,如果有 0 , 那就前功盡棄。
還有一個問題,如果乘起來的話, 最終的結果一定會很大, 所以對每一個數 取 log, log 之後乘就變成了加,
所以我們就找加最大的就好了,
取了log 之後, 我們要統計八個方向的前綴和, 還有 0 的位置, 最後就是 n*n 的掃描一遍, 找一個加起來最大的就好了,
八個方向,分成兩組, + 要四個方向, x 也是四個方向。
至於怎麼找, 對於每個位置,我要找 四個方向上0最近的長度,根據這個長度,計算出來加和, 找加和最大的,標記一下, 最後答案重新乘起來就好了,
反思:
通過了這個題,可以把 乘法變加法。 這是一個小技巧。
#include<bits/stdc++.h>
using namespace std;
const int N = 1009;
const int mod = 1e9 + 7;
const int dx[8] = {0,-1,-1,-1,0,1,1,1};
const int dy[8] = {-1,-1,0,1,1,1,0,-1};
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
int n,m;
double a[N][N],f[N][N][3][3];
int b[N][N];
int g[N][N][3][3];
void solve(){
int x,y;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j){
for (int k = 0; k < 3; ++k){
x = i + dx[k]; y = j + dy[k];
if (a[i][j] != -1) {
f[i][j][dx[k]][dy[k]] = f[x][y][dx[k]][dy[k]] + a[i][j];
g[i][j][dx[k]][dy[k]] = g[x][y][dx[k]][dy[k]] + 1;
} else {
f[i][j][dx[k]][dy[k]] = 0;
g[i][j][dx[k]][dy[k]] = 0;
}
}
}
for (int i = 1; i <= n; ++i)
for (int j = n; j > 0; --j){
for (int k = 3; k < 4; ++k){
x = i + dx[k]; y = j + dy[k];
if (a[i][j] != -1) {
f[i][j][dx[k]][dy[k]] = f[x][y][dx[k]][dy[k]] + a[i][j];
g[i][j][dx[k]][dy[k]] = g[x][y][dx[k]][dy[k]] + 1;
} else {
f[i][j][dx[k]][dy[k]] = 0;
g[i][j][dx[k]][dy[k]] = 0;
}
}
}
for (int i = n; i > 0; --i)
for (int j = n; j > 0; --j){
for (int k = 4; k < 7; ++k){
x = i + dx[k]; y = j + dy[k];
if (a[i][j] != -1) {
f[i][j][dx[k]][dy[k]] = f[x][y][dx[k]][dy[k]] + a[i][j];
g[i][j][dx[k]][dy[k]] = g[x][y][dx[k]][dy[k]] + 1;
} else {
f[i][j][dx[k]][dy[k]] = 0;
g[i][j][dx[k]][dy[k]] = 0;
}
}
}
for (int i = n; i > 0; --i)
for (int j = 1; j <= n; ++j){
for (int k = 7; k < 8; ++k){
x = i + dx[k]; y = j + dy[k];
if (a[i][j]!=-1) {
f[i][j][dx[k]][dy[k]] = f[x][y][dx[k]][dy[k]] + a[i][j];
g[i][j][dx[k]][dy[k]] = g[x][y][dx[k]][dy[k]] + 1;
} else {
f[i][j][dx[k]][dy[k]] = 0;
g[i][j][dx[k]][dy[k]] = 0;
}
}
}
}
void solve1(){
double ans = -1,tim;
int I = 1,J = 1,K = -1,T,tmp;
for (int i = 1; i <= n; ++i){
for (int j = 1; j <= n; ++j){
if (a[i][j] == -1) continue;
tmp = n;
tim = 0;
for (int k = 0; k < 8; k+=2)
tmp = min(tmp, g[i][j][dx[k]][dy[k]]);
for (int k = 0; k < 8; k += 2)
tim += f[i][j][dx[k]][dy[k]] - f[i+dx[k]*tmp][j+dy[k]*tmp][dx[k]][dy[k]];
tim -= 3*a[i][j];
if (tim > ans) {
ans = tim;
I = i, J = j, K = 0; T = tmp;
}
tmp = n;
tim = 0;
for (int k = 1; k < 8; k+=2)
tmp = min(tmp, g[i][j][dx[k]][dy[k]]);
for (int k = 1; k < 8; k += 2)
tim += f[i][j][dx[k]][dy[k]] - f[i+dx[k]*tmp][j+dy[k]*tmp][dx[k]][dy[k]];
tim -= 3*a[i][j];
if (tim > ans) {
ans = tim;
I = i, J = j, K = 1; T = tmp;
// logs(i,j,ans,tmp);
}
// logs(i,j,tmp);
}
}
long long Ans;
if (K == -1){
puts("0");
} else{
Ans = b[I][J];
for (int i = 1; i < T; ++i){
for (int j = K; j < 8; j+=2)
Ans = (Ans * b[I+dx[j]*i][J+dy[j]*i]) % mod;
}
printf("%lld\n",Ans);
}
}
int main(){
int x;
scanf("%d",&n);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j){
scanf("%1d",&x);
if (x) a[i][j] = log2(x); else a[i][j] = -1;
b[i][j] = x;
// logs(a[i][j],b[i][j]);
}
solve();
solve1();
return 0;
}