您的位置:首页 > 其它

紫书第7章 暴力求解法

2016-03-06 10:18 393 查看

例题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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",i*n,i,n);
ok = 1;
}
}
if(!ok)
pf("There are no solutions for %d.\n",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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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.\n",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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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\n",v);
for(i = 0;i<v;i++)
pf("1/%d = 1/%d + 1/%d\n",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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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("!\n")
#define INF 10000
#define MAXN 5010
#define MAX(a,b) a>b?a:b
#define blank pf("\n")
#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:\n",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\n",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\n",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\n",dist[ans]);
else pf("-1\n");
}


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\n",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)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: