之所以要写这两题的解题报告,原因就是这两题是比较好的搜索题,必须记录一下。
另外,最近做题状态也不行,看见题目后没想法,然后看别人解题报告了,突然发现自己想过这方面,但是没深入去想,然后就做不出来了。
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;
}