在一張n行
m列的方格地圖上放置一些守衛,每個守衛能守護上、左、右三個方向上相鄰的方格和自己所在的方格。如下圖,紅色的方格放置守衛,綠色的方格為該守衛守護的區域。
現在要求在地圖上放置若干個守衛,讓每個方格至少被乙個守衛守護(可以同時被多個守衛守護),但是有些方格上不能放置守衛(這個方格也需要被守護),求出最少需要多少個守衛才能滿足條件。
第一行輸入兩個整數
n, m
。接下來輸入乙個
n×m的矩陣。矩陣中元素為 0 表示該位置不能放置守衛,為 1 表示該位置能放置守衛。元素之間用空格隔開。
資料約定:
所有資料保證一定有一種方案滿足條件。
對於 20% 的資料: 1≤n,m≤5;
對於 50% 的資料: 1≤n≤20,1≤m≤10;
對於100% 的資料: 1≤n∗m≤1000,1≤m≤15。
輸出最少需要放置的守衛數量。
沒有打這個。。不過學校群有人發出來了。做了下,沒得交,大概思路應該是對的
做法:
我們做乙個dp,假設格仔移動到了圖中標記為1的地方,我們定義dp[now][sta]為當移動到這個格仔,而且(4,3,2,1,0)的狀態為sta的二進位制表示(順序也是一樣的,1表示已放置,0表示未放置),並且這個分界線以上的全部被放置了的最小放置守衛數,這個分界線是這樣的,當轉移到(i,j)時,分界線就是(i,0)(i,1)...(i,j)(i-1,j)(i-1,j+1)..(i-1,m-1)轉移比較簡單,初始化第乙個格仔的時候,我們知道第乙個格仔是沒有格仔的,但是我們把那裡當成已被守衛的格仔就好了所以就是dp[now][(1 << m+1) - 2] = 0,其他定義為-1。
舉個例子吧,下面的數字外面一層表示行列數,裡面的表示分界線
0 1 2 3
0 _ _ 3 4
1 0 1 2 _
圖1我們知道我們當前是轉移到(1,2)這個位置的,當這個位置可以放守衛,我們選擇放乙個守衛之後
0 1 2 3
0 _ _ _ 4
1 0 1 2 3
圖2現在轉移到下乙個格仔了,但是剛剛在(1,2)放了守衛,所以(1,1)(1,2)(1,3)(0,2)這四個位置的狀態都要變成了1,所以我們的sta需要或乙個(1 << (j-1))(1 << (j))(1 << (j+1))如果j-1小於0就不用了,(0,2)這個位置已經到了分界線外。所以如果當前格仔上面是沒有守衛的,就必須放乙個。因為我們定義的dp是分界線上必須全部被守衛。
如果當前格仔有守衛,我們可以選擇不放,如果不放的話我們需要把sta^(1 << (j + 1)),因為我們可以知道圖1的分界線3轉移到下乙個的圖2時,這個位置原本是沒有守衛的,所以要置為0。
這上面的轉移是一種情況,但是當轉移需要換行的時候,轉移的式子會有所改變,不過手推很容易推出來。
#include #includeview code#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using
namespace
std;
#define mp make_pair
#define rep(i,n) for(int i = 0; i < (n); i++)
#define per(i,n) for(int i = (n-1); i >= 0; i--)
#define rep1(i,n) for(int i = 1; i <= (n); i++)
#define per1(i,n) for(int i = (n); i > 0; i--)
#define pb push_back
#define mst(a,b) memset((a),(b),sizeof(a))typedef
long
long
ll;typedef unsigned
long
long
ull;
typedef pair
pii;
typedef vector
vi;typedef vector
vii;
const
int inf = 0x3f3f3f3f
;const ll inf = (1ull << 63) - 1
;const ll mod = 1000000007
;const
int n = 1e5 + 7
;const
double pi = acos(-1.0
);const
int maxn = (1
<< 16) + 7
;const ull hashmod = 29050993
;int dp[2
][maxn];
int maz[1005][15
];int
n, m;
inline
bool up(int sta, int
pos)
inline
void umin(int &a, int
b) inline
int set(int sta, int
pos)
return
sta;
}inline
int noset(int sta,int
pos)
//inline void show(int x)
intmain() }}
}int ans = dp[now][(1
<< (m + 1)) - 1
]; umin(ans,dp[nx][(
1<< (m + 1)) - 2
]); cout
<< ans <
}
計蒜客藍橋杯省賽模擬五
a.了解進製轉換原理 b.熟練掌握求數字中各個位置數的方法 includeusing namespace std int check int a return re int main void printf lld n ans return 0 c.做法一 費馬小定理的運用,若a和b互質,則a的 b...
計蒜客藍橋杯模擬賽心得
1.有趣的數字 我們稱乙個數是質數,而且數字 現了 55 的數字是有趣的。例如 5,59,457 都是有趣的,而 15,7 不是。求 1 到 100000 中有趣的數的個數。思路 其實很簡單,1 判斷是否為素數 2 位數是否含有5 注意點 剛開始以為要總數減去1,就是15這個數,後來才發現15不是素...
2019計蒜客藍橋杯模擬賽三
思路 一開始遞推推錯了,改用dfs includeusing namespace std int ans void dfs int x,int sum 記住i從x開始搜的這一剪枝,不僅僅可以提公升效率 更重要的是可以保證搜尋出來的結果不會重複,是有序的 for int i x i 10 sum i ...