POJ 1010 1020 DFS+剪枝
之所以要寫這兩題的解題報告,原因就是這兩題是比較好的搜尋題,必須記錄一下。
另外,最近做題狀態也不行,看見題目後沒想法,然後看別人解題報告了,突然發現自己想過這方面,但是沒深入去想,然後就做不出來了。
1.POJ 1010 題意晦澀難懂。從網上找了一個稍微能說清楚的題意
題意:
給出n種郵票,每種郵票有自己的面值(面值可能重複)
指定m種“總面值”,對每種“總面值”,求解滿足如下條件的組合以達到該“總面值”
(1) 所用郵票在n種中可以重複選取
(2) 所用郵票張數〈=4
(3) 儘量多的使用那個不同種類的郵票 Max (Stamp Types)
(4) 若有多種方案滿足(3),則選取張數最小的一種方案 Min (Stamp Num)
(5) 若有多種方案滿足(3)(4),則選取“最大面額”最高的一種方案。 Max(Heightest Value)
(6) 若有多種方案滿足(3)(4)(5) 則輸出 “tie” 1010
並且這題給的sample都還夾著註釋,不仔細看題還以為那些也需要輸入。
說一下思路吧,動態規劃是最優選擇,但是確實不好想,普遍的都是用DFS。
優化之一: 每種面值的郵票最多5種即可,如果有多的,就不需要了。比如一下子給了1 1 1 1 1 1 1 1,八個1,其實只需要5個就行,因為題目說了,最多4個,保留5個的目的就是為了多解準備的。再多也沒意義。這樣由於最多有25種郵票,也就是陣列到126就夠了。
然後dfs過程中如果選擇的郵票面額之和大於所需的,也剪掉,總數大於4的剪掉,剩下的貌似就沒什麼了。
dfs過程中,有一箇中間值,儲存著郵票的種類,總個數,最大面值,所選的郵票等,然後有一個最終結果,每次dfs出的中間值,如果優於最終結果就要更新最終結果,那麼怎樣算是tie呢,就要用1個變數標記一下,如果被更新了,就置這個變數為1,如果出現打平的,就把他++一下,這樣不斷的覆蓋這個變數即可。
另外,這道題目貌似給的序列都是有序的。
/* ID: sdj22251 PROG: subset LANG: C++ */ #include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <map> #include <string> #include <cstring> #include <cmath> #include <ctime> #define MAXN 100007 #define INF 1000000000 #define eps 1e-7 using namespace std; int size, x; int stamp[550]; int some; struct wwj { int cnt, num; int mx, ana[555]; void init() { cnt = 0; num = 0; mx = 0; memset(ana, 0, sizeof(ana)); } }tmp, ans; void init() { some = 0; tmp.init(); ans.init(); } void get() { tmp.cnt = 0; tmp.mx = 0; for(int i = 0; i < size; i++) { if(tmp.ana[i]) { tmp.cnt++; if(stamp[i] > tmp.mx) tmp.mx = stamp[i]; } } } void dfs(int index, int num, int sum) { if(num > 4) return; if(sum == x) { tmp.num = num; get(); if(tmp.cnt > ans.cnt) { ans = tmp; some = 1; } else if(tmp.cnt == ans.cnt) { if(tmp.num < ans.num) { ans = tmp; some = 1; } else if(tmp.num == ans.num) { if(tmp.mx > ans.mx) { ans = tmp; some = 1; } else if(tmp.mx == ans.mx) {some++;} } } return; } if(index >= size || sum > x) return; for(int i = 0; i <= 4; i++) { tmp.ana[index] += i; dfs(index + 1, num + i, sum + stamp[index] * i); tmp.ana[index] -= i; } } void output() { printf("%d ", x); if(some == 0) printf("---- none\n"); else if(some == 1) { printf("(%d):", ans.cnt); for(int i = 0; i < size; i++) { if(ans.ana[i] > 0) { for(int j = 0; j < ans.ana[i]; j++) printf(" %d", stamp[i]); } } printf("\n"); } else printf("(%d): tie\n", ans.cnt); } int main() { while(scanf("%d", &x) != EOF) { size = 0; stamp[size++] = x; while(scanf("%d", &x) != EOF && x) { int nt = 0; for(int i = 0; i < size; i++) if(stamp[i] == x) nt++; if(nt >= 5) continue; stamp[size++] = x; } while(scanf("%d", &x) != EOF && x) { init(); dfs(0, 0, 0); output(); } } return 0; }
2.poj 1020
又是一道搜尋題
題目大意是給出一個大的正方形,然後又給出了一群小正方形,問大的正方形能不能由這些小正方形組成,並且每個正方形都要用到。
思路是貪心+dfs
首先,對輸入的陣列,由於題目中說了,小正方形的邊長範圍是1~10,所以開個陣列記錄一下每個邊長的正方形個數,dfs的時候列舉就方便了。
然後用一個數組,len[i]表示第i行被覆蓋了len[i]列,而且是左邊無空隙的。
然後每次貪心找最小的len[i],因為這樣的話,達到最優解的可行性最大。在這個位置上,看下面和右面是否有足夠的位置,前提是左邊是填充好的,所以就要求這些位置的len[i]必須都是一樣的,然後就列舉邊長,往裡塞小正方形,找不到解就回溯。
網上有的貪心是不正確的,因為沒有考慮到一些比較特別的資料,而且poj的這道題資料還很弱。正確的做法應該是貪心+回溯,就能保證一定要到正確的答案。
/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 100007
#define INF 1000000000
#define eps 1e-7
using namespace std;
int n, x;
int len[55], cnt[15];
bool dfs(int deep)
{
if(deep == n) return true;
int mi = INF;
int pos = 0;
for(int i = 1; i <= x; i++)
{
if(mi > len[i])
{
mi = len[i];
pos = i;
}
}
for(int i = 1; i <= 10; i++)
{
if(cnt[i])
{
if(len[pos] + i <= x)
{
int width = 0;
for(int j = pos; j <= x; j++)
if(len[j] == len[pos]) width++;
else break;
if(width >= i)
{
cnt[i]--;
for(int j = pos; j < pos + i; j++)
len[j] += i;
if(dfs(deep + 1)) return true;
cnt[i]++;
for(int j = pos; j < pos + i; j++)
len[j] -= i;
}
}
}
}
return false;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &x);
memset(len, 0, sizeof(len));
memset(cnt, 0, sizeof(cnt));
scanf("%d", &n);
int area = 0, t;
for(int i = 0; i < n; i++)
{
scanf("%d", &t);
cnt[t]++;
area += t * t;
}
if(x * x == area && dfs(0))
puts("KHOOOOB!");
else puts("HUTUTU!");
}
return 0;
}