PAT (Top Level) Practice 1004 To Buy or Not to Buy - Hard Version(深搜枚举+众多剪枝)

1004 To Buy or Not to Buy - Hard Version (35 分)

Eva would like to make a string of beads with her favorite colors so she went to a small shop to buy some beads. There were many colorful strings of beads. However the owner of the shop would only sell the strings in whole pieces. Hence in some cases Eva might have to buy several strings to get all the beads she needs. With a hundred strings in the shop, Eva needs your help to tell her whether or not she can get all the beads she needs with the least number of extra beads she has to pay for.

For the sake of simplicity, let's use the characters in the ranges [0-9], [a-z], and [A-Z] to represent the colors. In sample 1, buying the 2nd and the last two strings is the best way since there are only 3 extra beads. In sample 2, buying all the three strings won't help since there are three R beads missing.

Input Specification:

Each input file contains one test case. Each case first gives in a line the string that Eva wants. Then a positive integer N (≤100) is given in the next line, followed by Nlines of strings that belong to the shop. All the strings contain no more than 1000 beads.

Output Specification:

For each test case, print your answer in one line. If the answer is Yes, then also output the least number of extra beads Eva has to buy; or if the answer is No, then also output the number of beads missing from all the strings. There must be exactly 1 space between the answer and the number.

Sample Input 1:

RYg5
8
gY5Ybf
8R5
12346789
gRg8h
5Y37
pRgYgbR52
8Y
8g

Sample Output 1:

Yes 3

Sample Input 2:

YrRR8RRrY
3
ppRGrrYB225
8ppGrrB25
Zd6KrY

Sample Output 2:

No 3

借鉴:http://www.voidcn.com/article/p-xlwxwqgc-mk.html

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1e3 + 10;
const int size = 62;
const int INF = 0x7FFFFFFF;
const int limit = 178;
char s[maxn];
int n, f[size], now[size], out[maxn][size], ans, cost, cnt; //now[i]表示当前已拿的字符个数。

struct point
{
  int f[size], cost, use;
  bool operator<(const point&a)const
  {
    return use > a.use;
  }
}a[maxn];

int get(char c)
{
  if ('0' <= c&&c <= '9') return c - '0';
  if ('a' <= c&&c <= 'z') return c - 'a' + 36;
  if ('A' <= c&&c <= 'Z') return c - 'A' + 10;    //当字符串较多时,map就会很耗时间,所以用数组来存字符个数。
}                                                 //同时字符种类很少时,用数组便于枚举
//相比简易版本,多条意味着有最值问题,用深搜枚举每一种情况,找到这个最小值
void dfs(int x, int c)    //dfs就是枚举所有已知的字符串。深搜枚举也是取与不取实现全部枚举
{
  cnt++;                  //A*减枝的关键是能估算出这个limit的值。
  if(cnt>limit) return;   //对层数的估值
  if (c >= ans) return;   //对结果大小的估值,因为是取最小值,所以一旦大于就舍弃。

  bool flag1 = true, flag2 = false;                                        //now[i]一开始都为0
  for (int i = 0; i < size; i++) if (now[i] + out[x][i] < f[i]) return;    //如果最多的还小,肯定是不行,扩大剪枝
  for (int i = 0; i < size; i++) if (now[i] < f[i]) { flag1 = false; break; }
  if (flag1)   {ans = c; return;}                                          //分析剪枝
                                                                          //如果所有now[i]都不比f[i]小的话就不用再找下一条了,已经找完,return
  if (x == n)   return;  //找完所有字符串。
  for (int i = 0; i < size; i++) if (now[i] < f[i] && a[x].f[i]) flag2 = true;   //当前这条链有想要的字符且想要的没取完
  if (flag2)
 {
     for (int i = 0; i < size; i++) now[i] += a[x].f[i];     //将这条链上所有想要的字符拿走
     dfs(x + 1, c + a[x].cost);                              //取这条进行下一条的搜索
     if (cnt > limit) return;
     for (int i = 0; i < size; i++) now[i] -= a[x].f[i];    //枚举所有情况就要有回溯
  }
  dfs(x + 1, c);   //不取这条,进行下一条
  if (cnt > limit) return;
}

int main()
{
  scanf("%s%d", s, &n);  cost = strlen(s);
  for (int i = 0; s[i]; i++) f[get(s[i])]++;   //将字母转成数字,就能用数组表示map的功能记录字母的个数
  for (int i = 0; i < n; i++)
  {
    scanf("%s", s);  a[i].cost = strlen(s);
    for (int j = 0; s[j]; j++) a[i].f[get(s[j])]++, out[0][get(s[j])]++;   //统计所有字符串相应的字符个数
    for (int j = 0; j < size; j++) a[i].use += min(f[j], a[i].f[j]); //最小的字母数的总和
  }
  for (int i = 0; i < size; i++)
  if (f[i]>out[0][i]) ans += f[i] - out[0][i];
  if (ans) printf("No %d\n", ans);
  else
  {
    sort(a, a + n);     //use值从大到小排序,减少搜索次数
    for (int i = n - 1; i >= 0; i--)
    for (int j = 0; j < size; j++)
    out[i][j] = out[i + 1][j] + a[i].f[j];
    ans = INF;
    dfs(0, 0);
    printf("Yes %d\n", ans - cost);
  }
  return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值