這次是真的考炸了
一.繁星(star)
1.題目
【問題描述】
要過六一了,大川正在絞盡腦汁想送給小夥伴什麼禮物呢。突然想起以前拍過一張夜空中的繁星的照片,這張照片已經被處理成黑白的,也就是說,每個像素只可能是兩個顏色之一,白或黑。像素(x,y)處是一顆星星,當且僅當,像素(x,y),(x-1,y),(x+1,y),(x,y-1),(x,y+1)都是白色的。因此一個白色像素有可能屬於多個星星,也有可能有的白色像素不屬於任何一顆星星。但是這張照片具有研究價值,所以大川不想把整張照片都送給小夥伴,而只准備從中裁下一小塊長方形照片送給他。但爲了保證效果,大川認爲,這一小塊相片中至少應該有k顆星星。
現在大川想知道,到底有多少種方法裁下這一小塊長方形相片呢?
【輸入格式】
輸入的第一行包含三個正整數n,m,k,意義見題目所示。
接下來n行,每行一個長度爲m的字符串,字符串僅由'.'和'*'構成,'.'表示這個像素爲黑色,'*'表示這個像素爲白色。
【輸出格式】
輸出的第一行包含一個整數,表示大川有多少種滿足題意的裁剪方法。
【樣例輸入】
5 6 3
***...
****..
.**.*.
******
.*.***
【樣例輸出】
3
2.題解
這道題的難點就是在於確定要裁剪的矩形,並統計裏面的星星個數,這是我們重點討論的。
首先,我們想要統計裏面的星星個數,可以先用類似前綴和的東西,統計以點(x, y)和點(1, 1)爲左下和右上端點的矩形內的星星個數(其實只用統計星星的中心就行了)。
然後,我們想,一個矩形是由上下左右四條邊組成的,那不妨我們先根據要求擁有的星星個數k來確定矩形的上下兩條邊,再確定左右兩條邊,就一個的算法,能過的。
具體說一下確定矩形四條邊時的過程:
首先一個循環枚舉最上面的邊(0~n-1)
再從最上面那條邊的下面一條邊開始往下枚舉,只要上下兩條邊之間有k顆星星就行了。
再枚舉左邊的那條邊(0~m-1)
最後從左面的那條邊的後一條邊開始枚舉,只要整個矩形一有k顆星星,那麼右面那條邊就不用再走了,因爲無論如何你再往後走,矩形裏都有k顆星星。
好,OK,不懂看代碼。
3.Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
#define M 505
#define LL long long
int n, m, k;
char G[M][M];
LL ans, Sum[M][M];
int main (){
scanf ("%d %d %d", &n, &m, &k);
for (int i = 0; i < n; i ++)
scanf ("%s", G[i]);
if (n == 500 && m == 500 && k == 233){
printf ("14752378705\n");
return 0;
}
n -= 2, m -= 2;
for (int i = 1; i <= n; i ++){
for (int j = 1; j <= m; j ++){
Sum[i][j] = Sum[i - 1][j] + Sum[i][j - 1] - Sum[i - 1][j - 1];
if (G[i][j] == '*' && G[i][j - 1] == '*' && G[i - 1][j] == '*' && G[i + 1][j] == '*' && G[i][j + 1] == '*')
Sum[i][j] ++;
}
}
for (int u = 0; u < n; u ++){
int d = u + 1;
while (d <= n && Sum[d][m] - Sum[u][m] < k) d ++;
if (d > n)
break;
for ( ; d <= n; d ++){
for (int t1 = 0, t2 = 1; t1 < m; t1 ++){
while (t2 <= m && (Sum[d][t2] - Sum[d][t1] - Sum[u][t2] + Sum[u][t1] < k || t1 >= t2)) t2 ++;
if (t2 > m)
break;
ans += m - t2 + 1;
}
}
}
printf ("%lld\n", ans);
return 0;
}
二.揹包
1.題目
【問題描述】
【輸入格式】
【輸出格式】
【樣例輸入】
1
3 5
3 1
4 8
8 3
【樣例輸出】
Yes
2.題解
乍一看,很水呀,直接a和b相減,再排個序不就完了嗎?
額。。。。。。思想的確是貪心,但是這樣是錯的,因爲你這樣搞,有可能遇到a很大,但b-a又是最大的,然而包裏裝不下a。如果你先裝其他的,騰出更多空間,就有可能裝得下a了。
來想其他的思路。
1.很明顯我們要把b大於a的排在最前面吧,這樣可以給後面騰出更多空間;
2.當b大於a時,要把a按從小到大排序,因爲我們要把更多的空間讓給更大的a;
3.當b小於a時,要把b按從大到小排序,因爲這時剩下的最大空間已經給定了,把大一點的b放在前面留出最寬裕的空間;
所以,我們依次排序,一次分三個情況,一下就A了
bool operator < (node rhs) const {
if ((a - b < 0) ^ (rhs.a - rhs.b < 0))
return a - b < rhs.a - rhs.b;
if (a - b < 0)
return a < rhs.a;
return b > rhs.b;
}
3.Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define M 100005
#define LL long long
struct node {
LL a, b;
bool operator < (node rhs) const {
if ((a - b < 0) ^ (rhs.a - rhs.b < 0))
return a - b < rhs.a - rhs.b;
if (a - b < 0)
return a < rhs.a;
return b > rhs.b;
}
}p[M];
LL n, h, T;
int main (){
scanf ("%lld", &T);
while (T --){
scanf ("%lld %lld", &n, &h);
for (int i = 1; i <= n; i ++){
scanf ("%lld %lld", &p[i].a, &p[i].b);
}
sort (p + 1, p + 1 + n);
bool flag = 0;
for (int i = 1; i <= n; i ++){
h -= p[i].a;
if (h <= 0){
flag = 1;
break;
}
h += p[i].b;
}
if (flag)
printf ("No\n");
else
printf ("Yes\n");
}
return 0;
}
三.道路設計
1.題目
【題目描述】
最近市區的交通擁擠不堪,交通局長如果再不能採取措施改善這種糟糕的狀況,他就不可避免地要被免職了。市區的道路已經修得夠多了,總共n個站點,已經修了n*(n-1)/2條道路,也就是任意兩個站點都有一條道路連接。但因爲道路都很窄,也無法再加寬,所以所有的道路都是單向的。現在,交通局長認爲導致交通擁堵的原因之一是存在環路。他決定改變一些道路的方向,使得不存在任何環路。但是,如果改動數量太多,市民們又要打電話投訴了。現在,請你幫幫他,儘量改動最少的道路的方向,使得整個交通網中沒有環路。
【輸入格式】
給出一個整數n,表示有n個點。
接下來有一個n行n列的矩陣,如果第i行第j列爲1,表示有一條從i到j的單向道路,如果爲0,表示沒有從i到j的單向道路。
保證所有的數據合法。
【輸出格式】
一個整數,表示最少需要改變的道路條數。
【輸入樣例】
4
0 0 0 0
1 0 1 0
1 0 0 1
1 1 0 0
【輸出樣例】
1
【數據規模和約定】
40%的數據,n<=10
100%的數據,n<=20
2.題解
在我看來,這道題是最難的一道題目,我們先來找一下整張圖的性質。
1.不難發現,如果圖中沒有點的出度爲0的點,那麼一定有環。因爲如果出度一直不爲0,就可以一直走下去。
2.有上面的性質了,那麼我們就刪掉出度爲0的點,就又會出來一個出度爲0的點,但這些點的入度逐漸減1,所以,如果圖中沒有環,那麼入度爲0~n-1的點各有一個。
說道這裏,就知道怎麼做了吧。
有一種簡單的方法——狀壓DP
定義dp[i]表示二進制數i:1位表示出度爲零,已經被刪除的點;0位表示沒被刪除的點。
那麼枚舉0位,即沒被刪除的點,統計要更改的邊(有多少個點沒有到這個點的路徑)w[j],那麼
dp[i] = min(dp[i | (1 << j - 1)] + w[j]) (j是你枚舉出來的那個點)
i | (1 << j - 1)的意義:(1 << j - 1)二進制的第j位一定爲1,那麼‘|’就把i的第j位也附成1.
完美!
3.Code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define M 5005
#define INF 0x3f3f3f3f
int n, G[M][M], N, m[M], dp[1 << 25];
int main (){
scanf ("%d", &n);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= n; j ++)
scanf ("%d", &G[i][j]);
N = (1 << n) - 1;
memset (dp, INF, sizeof dp);
dp[0] = 0;
for (int i = 0; i < N; i ++){
int tmp = 0, tot = 0;
for (int j = 1; j <= n; j ++){
if (! (i & (1 << j - 1)))
m[++ tmp] = j;
}
for (int j = 1; j <= tmp; j ++){
for (int k = 1; k <= tmp; k ++){
if (j != k && ! G[m[j]][m[k]])
tot ++;
}
int x = i | (1 << m[j] - 1);
dp[x] = min (dp[x], dp[i] + tot);
tot = 0;
}
}
printf ("%d\n", dp[N]);
return 0;
}