紫书第7章 暴力求解法

例题7-1 除法(Division, UVa 725)

一定要注意数组初始化的位置

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue

char buf[6];
int used[10];

bool check(char* a)
{
     mem(used,0);
     for(int i =0;i<10;i++)
     {
          used[a[i]-'0']=1;
     }
     for(int i = 0;i<10;i++)
     {
          if(!used[i]) return false;
     }
     return true;
}

int main()
{
     int n,i,j,kase=0;
     while(sf("%d",&n)==1 && n)
     {
          int ok=0;
          if(kase++)
               blank;
          for(i=1;i<=98765;i++)
          {
               spf(buf,"%05d%05d",i*n,i);
               int len = strlen(buf);
               if(len>10) break;
               if(check(buf))
               {
                    pf("%05d / %05d = %d
",i*n,i,n);
                    ok = 1;
               }
          }
          if(!ok)
               pf("There are no solutions for %d.
",n);
     }
}

例题7-2 最大乘积(Maximum Product, UVa 11059)

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue

int buf[20];

int main()
{
     int n,i,j,kase=0,cnt=1;
     while(sf("%d",&n)==1)
     {
          for(i=0;i<n;i++)
          {
               sf("%d",&buf[i]);
          }
          if(kase++) blank;

          long long ma = 0;
          for(i=0;i<n;i++)
          {
               long long res = 1;
               for(j=i;j<n;j++)
               {
                    res*=buf[j];
                    if(res>ma)
                         ma = res;
               }
          }
          pf("Case #%d: The maximum product is %I64d.
",cnt++,ma);
     }
}

例题7-3 分数拆分(Fractions Again?!, UVa 10976)

尽量用余数%判断一个double是否是整数

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue

int x[10011],y[10011];

int main()
{
     int n,i,j,kase=0;
     while(sf("%d",&n)==1)
     {
          int v=0,cnt=0;
          mem(x,0);
          mem(y,0);
          for(i=n+1;i<=2*n;i++)
          {
               if(i*n % (i-n)==0)
               {
                    x[v]=i*n/(i-n);
                    y[v++]=i;
               }

          }
          pf("%d
",v);
          for(i = 0;i<v;i++)
               pf("1/%d = 1/%d + 1/%d
",n,x[i],y[i]);
     }
}

生成1~n的排列

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue

int a[10011],b[10011];

void NP(int n,int* a,int cur)
{
     if(cur==n)
     {
          for(int i = 0;i<n;i++)
               pf("%d ",a[i]);
          blank;
     }
     else
     {
          for(int i =1;i<=n;i++)
          {
               int ok = 1;
               for(int j = 0;j<cur;j++)
               {
                    if(a[j]==i) ok = 0;
               }
               if(ok)
               {
                    a[cur] = i;
                    NP(n,a,cur+1);
               }
          }
     }
}

int main()
{
     int n,i,j,kase=0;
     while(sf("%d",&n)==1)
     {
          NP(n,a,0);
     }
}

7.2.2 生成可重集的排列

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue


void NP(int n,int* p,int* a,int cur)
{
     if(cur==n)
     {
          for(int i = 0;i<n;i++)
               pf("%d ",a[i]);
          blank;
     }
     else
     {
          for(int i =0;i<n;i++)
          {
               if(!i || p[i-1]!=p[i])
               {
                    int c1=0,c2=0;
                    for(int j = 0;j<cur;j++)
                    {
                         if(a[j]==p[i]) c1++;
                    }
                    for(int j = 0;j<n;j++)
                    {
                         if(p[j]==p[i]) c2++;
                    }
                    if(c1<c2)
                    {
                         a[cur] = p[i];
                         NP(n,p,a,cur+1);
                    }
               }
          }
     }
}

int main()
{
     int n,i,j,kase=0;

     int A[1000],P[1000];

     while(sf("%d",&n)==1)
     {
          for(i = 0;i<n;i++)
               sf("%d",&P[i]);
          sort(P,P+n);
          NP(n,P,A,0);
     }
}

/*
5
6 5 8 7 2
5
6 5 8 5 2
*/

库函数next_permutation

解决了重复问题,需要排序

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue

int main()
{
     int n,i,j,kase=0;

     int A[1000],P[1000];

     while(sf("%d",&n)==1)
     {
          for(i = 0;i<n;i++)
               sf("%d",&P[i]);
          sort(P,P+n);
          do
          {
               for(i=0;i<n;i++)
                    pf("%d ",P[i]);
               blank;
          }
          while(next_permutation(P,P+n));
     }
}

/*
5
6 5 8 7 2
5
6 5 8 5 2
*/

7.3 子集生成

7.3.1 增量构造法

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <cctype>
#include <vector>
#include <iterator>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define mem(a,b) memset(a,b,sizeof(a))
#define pf printf
#define sf scanf
#define spf sprintf
#define pb push_back
#define debug printf("!
")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("
")
#define LL long long
#define ALL(x) x.begin(),x.end()
#define INS(x) inserter(x,x.begin())
#define pqueue priority_queue

void subset(int n,int* A,int cur)
{
     int i;
     for(i=0;i<cur;i++) pf("%d ",A[i]);
     blank;
     int s = cur?A[cur-1]+1:0;
     for(i=s;i<n;i++)
     {
          A[cur] = i;
          subset(n,A,cur+1);
     }
}

int main()
{
     int n,i,j,kase=0;

     int A[1000],P[1000];

     while(sf("%d",&n)==1)
     {
          subset(n,A,0);
     }
}

/*
5
6 5 8 7 2
5
6 5 8 5 2
*/

7.3.2 位向量法

void subset(int n,int* B,int cur)
{
     int i;
     if(cur==n)
     {
          for(i=0;i<cur;i++)
          {
               if(B[i])
                    pf("%d ",i);
          }
          blank;
          return;
     }
     B[cur] = 1;
     subset(n,B,cur+1);
     B[cur] = 0;
     subset(n,B,cur+1);
}

int main()
{
     int n,i,j,kase=0;

     int B[1000],P[1000];

     while(sf("%d",&n)==1)
     {
          subset(n,B,0);
     }
}

/*
5
6 5 8 7 2
5
6 5 8 5 2
*/

7.3.3 二进制法

void subset(int n,int s)
{
     for(int i =0;i<n;i++)
     {
          if(s&(1<<i)) pf("%d ",i);
     }
     blank;
}

int main()
{
     int n,i,j,kase=0;

     int B[1000],P[1000];

     while(sf("%d",&n)==1)
     {
          for(int i = 0;i<(1<<n);i++)
               subset(n,i);
     }
}

/*
5
6 5 8 7 2
5
6 5 8 5 2
*/

7.4.1 八皇后问题

void sh(int cur,int n)
{
     if(cur==n)
          tot++;
     for(int i = 0;i<n;i++)
     {
          int ok = 1;
          C[cur] = i;
          for(j=0;j<cur;j++)
          {
               if(C[j] == C[cur] || cur-C[cur]==j-C[j] || cur+C[cur]==j+C[j])
               {
                    ok = 0;
                    break;
               }
          }
          if(ok)
               sh(cur+1,n);
          
     }
}

例题7-4 素数环(Prime Ring Problem, UVa 524)

很简单的问题,回溯法,注意细节

int n;
int isp[50],A[20],vis[20];

int is_prime(int a)
{
     int i;
     for(i=2;i<=a/2;i++)
     {
          if(a%i==0) return 0;
     }
     return 1;
}

void init()
{
     for(int i =3;i<32;i++)
     {
          isp[i] = is_prime(i);
     }
}

void dfs(int cur)
{
     int i;
     if(cur==n && isp[A[0]+A[n-1]])
     {
          for(i=0;i<n;i++)
               pf("%d ",A[i]);
          blank;
     }
     for(i=2;i<=n;i++)
     {
          if(!vis[i] && isp[A[cur-1]+i])
          {
               vis[i] = 1;
               A[cur] = i;
               dfs(cur+1);
               vis[i] = 0;
          }
     }
}

int main()
{
     int kase = 0;
     init();
     A[0]=1;
     while(sf("%d",&n)==1)
     {
          if(kase++)
               blank;
          pf("Case %d:
",kase+1);
          mem(vis,0);
          dfs(1);
     }
}

例题7-5 困难的串(Krypton Factor, UVa 129)

枚举连续不重复子串只需要判断当前串的后缀

int n,L,cnt;
int S[100];

int dfs(int cur)//返回0表示已经得到解,无须继续搜索
{
     int i,j,k;
     if(cnt++==n)
     {
          for(i=0;i<cur;i++)
          {
               if(i%64==0 && i)
                    blank;
               else if(i%4==0 && i)
                    pf(" ");
               pf("%c",S[i]+'A');
          }
          blank;
          pf("%d
",cur);
          return 0;
     }
     for(i=0;i<L;i++)
     {
          S[cur] = i;
          int ok = 1;
          for(j=1;j*2<=cur+1;j++) //尝试长度为j*2的后缀
          {
               int eq = 1;
               for(k=0;k<j;k++)//检查后一半是否等于前一半
               {
                    if(S[cur-k]!=S[cur-k-j])
                    {
                         eq = 0;break;
                    }
               }
               if(eq)
               {
                    ok=0;break;
               }
          }
          if(ok)
          {
               if(!dfs(cur+1)) return 0;
          }
     }
     return 1;
}

int main()
{
     int kase = 0;
     while(sf("%d%d",&n,&L)==2 && n)
     {
          cnt=0;
          mem(S,0);
          dfs(0);
     }
}

例题7-6 带宽(Bandwidth, UVa 140)

简单回溯,主要是输入麻烦,弄清题意

有一个方法值得借鉴,想得到输入的字母的排序,可以先记录出现的字母,再导入到一个数组,以空间换时间

int ahl[27],node[9],save[9],mp[27][27];

char a[500];

int main()
{
     while(gets(a))
     {
          int i,j,c,cnt=0,mx=10,flag=0;
          if(!strcmp(a,"#")) break;
          int len = strlen(a);
          mem(mp,0);
          mem(ahl,0);
          mem(save,0);
          for(i=0;i<len;i++)
          {
               if(isalpha(a[i]))
               {
                    ahl[a[i]-'A']=1;
               }
               if(a[i]==':')
               {
                    c=a[i-1]-'A';
                    flag = 1;
               }
               else if(flag&&isalpha(a[i]))
               {
                    mp[c][a[i]-'A']=mp[a[i]-'A'][c]=1;
               }
               else
                    flag = 0;
          }

          for(i=0;i<26;i++)
          {
               if(ahl[i])
                    node[cnt++]=i;
          }

          do
          {
               int num = 0;
               for(i=0;i<cnt;i++)
               {
                    for(j=i+1;j<cnt;j++)
                    {
                         if(mp[node[i]][node[j]] && abs(i-j)>num)
                              num=abs(i-j);
                    }
                    if(num>mx)//剪枝
                         break;
               }
               if(mx>num)
               {
                    mx=num;
                    memcpy(save,node,sizeof(node));
               }

          }
          while(next_permutation(node,node+cnt));
          for(i=0;i<cnt;i++)
               pf("%c ",save[i]+'A');
          pf("-> %d
",mx);
     }
}

例题7-7 天平难题(Mobile Computing, ACM/ICPC Tokyo 2005, UVa1354)

7.5 路径寻找问题

八数码问题

const int maxstate = 1000000;

typedef int state[9];
state st[maxstate],goal;
int dist[maxstate];
//如果需要打印方案,可以在这里加一个"父亲编号"数组 int fa[maxstate]
const int dx[] = {1,-1,0,0}; const int dy[] = {0,0,1,-1};int bfs() { int i; int ft = 1,rr=2;//0为无解 init_lookup_table(); while(ft<rr) { state& s = st[ft]; if(!memcmp(s,goal,sizeof(goal))) return ft; int z,newx,newy,newz; for(z=0;z<9;z++) if(!s[z]) break; int x = z%3,y=z/3; for(i=0;i<4;i++) { newx = x+dx[i]; newy = y+dy[i]; newz = newy*3+newx; if(newx<3 && newx>=0 && newy<3 && newy>=0) { state& t = st[rr]; memcpy(&t,&s,sizeof(s)); t[z]=s[newz]; t[newz]=s[z]; dist[rr]=dist[ft]+1; if(try_to_insert(rr)) rr++; } } ft++; } return 0; } int main() { int i; for(i=0;i<9;i++) sf("%d",&st[1][i]); for(i=0;i<9;i++) sf("%d",&goal[i]); int ans = bfs(); if(ans) pf("%d ",dist[ans]); else pf("-1 "); }

bfs需要判重,如果开9维数组太大,一般有三种解决方法:

一:set

最方便,时间效率最低,一般做跳板

set<int> vis;

void init_lookup_table()
{
     vis.clear();
}

int try_to_insert(int a)
{
     int v= 0;
     for(int i =0;i<9;i++)
          v=v*10+st[a][i];
     if(vis.count(v)) return 0;
     vis.insert(v);
     return 1;
}

二:把排列“变成”整数,然后只开一个一维数组

int vis[362880],fact[9];//362880=9!

void init_lookup_table()
{
     fact[0] = 1;
     for(int i =1;i<9;i++) fact[i] = fact[i-1]*i;
}

int try_to_insert(int a)
{
     int code = 0;
     for(int i =0;i<9;i++)
     {
          int cnt = 0;
          for(int j = i+1;j<9;j++)
          {
               if(st[a][i]>st[a][j]) cnt++;
          }
          code+=fact[8-i]*cnt;
     }
     if(vis[code]) return 0;
     return vis[code] = 1;
}

尽管原理巧妙,时间效率也非常高,但编码解码法的适用范围并不大:如果隐式图的总
结点数非常大,编码也将会很大,数组还是开不下。

三:使用哈希(hash)技术

const int hashsize = 1000003;
int head[hashsize], next[maxstate];
void init_lookup_table( ) { memset(head, 0, sizeof(head)); }
int hash(State& s){
int v = 0;
for(int i = 0; i < 9; i++) v = v * 10 + s[i];//把9个数字组合成9位数
return v % hashsize; //确保hash函数值是不超过hash表的大小的非负整数
}
int try_to_insert(int s){
int h = hash(st[s]);
int u = head[h]; //从表头开始查找链表
while(u){
if(memcmp(st[u],st[s], sizeof(st[s]))==0)return 0; //找到了,插入失败
u = next[u]; //顺着链表继续找
}
next[s] = head[h]; //插入到链表中
head[h] = s;
return 1;
}

哈希表的执行效率高,适用范围也很广。除了BFS中的结点判重外,还可以用到其他需
要快速查找的地方。不过需要注意的是:在哈希表中,对效率起到关键作用的是哈希函数。
如果哈希函数选取得当,几乎不会有结点的哈希值相同,且此时链表查找的速度也较快;但
如果冲突严重,整个哈希表会退化成少数几条长长的链表,查找速度将非常缓慢。有趣的
是,前面的编码函数可以看作是一个完美的哈希函数,不需要解决冲突。不过,如果事
先并不知道它是完美的,也就不敢像前面一样只开一个vis数组。哈希技术还有很多值得探
讨的地方,建议读者在网上查找相关资料。

例题7-8 倒水问题(Fill, UVa 10603)

struct node
{
     int v[3],dist;
     bool operator <(const node& rhs)
     {
          return dist>rhs.dist;
     }
};

int cap[3],vis[205][205],ans[205];

void update(const node& u)
{
     for(int i =0;i<3;i++)
     {
          int d = u.v[i];
          if(ans[d]<0 || u.dist<ans[d]) ans[d]=u.dist;
     }
}

void bfs(int a,int b,int c,int d)
{
     pqueue<node> q;
     cap[0]=a;cap[1]=b;cap[2]=c;
     node start;
     start.v[0]=0;start.v[1]=0;start.v[2]=c;
     q.push(start);
     mem(vis,0);
     mem(ans,-1);
     vis[0][0]=1;
     while(!q.empty())
     {
          node t = q.top();q.pop();
          update(t);
          for(int i =0;i<3;i++)
          {
               for(int j = 0;j<3;j++) if(i!=j)
               {
                    if(t.v[i]==0 || t.v[j]==cap[j]) continue;
                    int amount = min(cap[j],t.v[i]+t.v[j])-t.v[j];

                    node t2;
                    memcpy(&t2,&t,sizeof(t));
                    t2.v[i]-=amount;
                    t2.v[j]+=amount;
                    t2.dist=t.dist+amount;
                    if(!vis[t2.v[0]][t2.v[1]])
                    {
                         vis[t2.v[0]][t2.v[1]]=1;
                         q.push(t2);
                    }
               }
          }
     }
     while(d>=0)
     {
          if(ans[d]>=0)
          {
               pf("%d %d
",ans[d],d);
               return;
          }
          d--;
     }

}

int main()
{
     int a,b,c,d;
     int t;
     sf("%d",&t);
     while(t--)
     {
          sf("%d%d%d%d",&a,&b,&c,&d);
          bfs(a,b,c,d);
     }
}

例题7-9 万圣节后的早晨(The Morning after Halloween, Japan 2007, UVa1601)

原文地址:https://www.cnblogs.com/qlky/p/5246653.html