poj 几道简单的搜索题(三)
题目:poj 2531
题意:
给出n(n<=20)个点,及点与点之间的权值,把这n个点划分成两个集合,使这两个集合中点与点之间的边权和最小?
分析:
n=20,暴力枚举的时间复杂度才O(2^20*C),C是求和的时间常数。2000ms的时间足够了。暴力枚举的话可以子集枚举和递归(时间接近1000ms)。
这题递归的话可以剪枝,可以优化到32ms,方法是参考:http://blog.csdn.net/martin31hao/article/details/8098302
#include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int N=22; int n,ans; int w[N][N]; bool in[N]; void dfs(int k,int sum) { in[k]=1; //0表示在0集合,1表示在1集合 int t=sum; for(int i=0;i<n;i++) if(in[i])t-=w[k][i]; else t+=w[k][i]; if(t>ans)ans=t; for(int i=k+1;i<n;i++) //看下一个点 if(t>sum){ //剪枝,只有t>sum才考虑以后的选择情况 dfs(i,t); //把i放到1集合 in[i]=0; //不放 } } int main() { memset(in,0,sizeof(in)); scanf("%d",&n); for(int i=0;i<n;i++) for(int j=0;j<n;j++)scanf("%d",&w[i][j]); ans=0; dfs(0,0); printf("%d\n",ans); return 0; }题目:poj 1416
题意:
整数划分问题,要求把所给整数划分,使得划分得到的的数的和尽量接近一个目标数字。
分析:
枚举划分的个数,dfs分配划分的位置,然后把划分的数加起来求和即可。
我是用一个string保存划分位置,最后再按照string保存的划分位置再求和。
剪枝的话应该是每划分一次就把所得到的和,与未划分的数加起来,然后与已经得到最优解比较,判断是否剪枝即可。
我的代码没优化,懒得改了QAQ。
#include<cstdio> #include<iostream> #include<stack> #include<vector> #include<cstring> using namespace std; int n,ans,total,ansnum,len; vector<int>vec; string as; void dfs(int tot,int id,int k,string s) { if(tot==0){ int i=0,sum=0; int pre=0; for(;i<=id;i++){ int x=s[i]-'0'; int j=x-1,w=1,num=0; while(j>=pre){ num+=vec[j--]*w; w*=10; } pre=x; sum+=num; } if(sum>ans&&sum<=total)ans=sum,ansnum=1,as=s,len=id; else if(sum==ans)ansnum++; return; } for(int i=k+1;i<vec.size();i++){ //枚举划分的位置 string t=s; t[id]=(char)(i+'0'); dfs(tot-1,id+1,i,t); } } void change(int x) { stack<int>s; while(x){ s.push(x%10); x/=10; } while(!s.empty())vec.push_back(s.top()),s.pop(); } int main() { while(~scanf("%d%d",&total,&n)&&(total+n)){ vec.clear(); ans=-1; change(n); string s="1234567"; for(int i=0;i<vec.size();i++){ //枚举划分的个数 s[i]=(char)('0'+vec.size()); dfs(i,0,0,s); } if(ans==-1)printf("error\n"); else if(ansnum>1)printf("rejected\n"); else{ printf("%d",ans); int i=0,sum=0; int pre=0; for(;i<=len;i++){ int x=as[i]-'0'; int j=x-1,w=1,num=0; while(j>=pre){ num+=vec[j--]*w; w*=10; } pre=x; printf(" %d",num); } printf("\n"); } } return 0; }
题目:poj 2676
题意:
完成数独9*9,使每一行,每一列,每一小块3*3,都是由1-9组成
分析:
把没放数的位置先找出来,然后dfs一个个放数。用三个数组标记一下以放数的行,列和块。
看Discuss说反搜更快。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int N=11; typedef pair<int,int>pii; pii q[N*N]; bool cow[N][N],row[N][N],blo[N][N]; //列,行,块 int a[N][N]; int id[9][9]={0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2, //每个位置所属的块 3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5, 6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8}; bool flag; int cnt; bool dfs(int s) { if(flag)return 1; if(s==cnt){ flag=1;return 1; } bool ok=1; int r=q[s].first,c=q[s].second; for(int k=1;k<=9;k++){ if(!row[r][k]&&!cow[c][k]&&!blo[id[r][c]][k]){ a[r][c]=k; row[r][k]=cow[c][k]=blo[id[r][c]][k]=1; if(dfs(s+1))return 1; row[r][k]=cow[c][k]=blo[id[r][c]][k]=0;//a[r][c]=0; } } return 0; } int main() { int T;scanf("%d",&T); char s[11]; while(T--){ memset(row,0,sizeof(row)); memset(cow,0,sizeof(cow)); memset(blo,0,sizeof(blo)); flag=0; cnt=0; for(int i=0;i<9;i++){ scanf("%s",s); for(int j=0;j<9;j++){ a[i][j]=s[j]-'0'; int k=a[i][j]; if(k!=0)row[i][k] =cow[j][k] =blo[id[i][j]][k]=true; else q[cnt++]=make_pair(i,j); } } dfs(0); for(int i=0;i<9;i++){ for(int j=0;j<9;j++)printf("%d",a[i][j]); printf("\n"); } } return 0; }
题目:poj 1129
题意:
图的染色问题,相邻点不能染同一种颜色,问需要最少颜色数?
分析:
dfs,依次枚举每个点,给他分配一种可以染的颜色,然后这种颜色从与它相邻的点的可染色中删除,然后再枚举下一个点,继续同样操作。
#include<cstdio> #include<cstring> #include<vector> using namespace std; const int N=30; vector<int>g[N]; //存边 int n; bool vis[N][N]; void dfs(int u) { if(u==n)return; int i; for(i=0;i<n;i++){ if(!vis[u][i]){vis[u][i]=1;break;} //找一种可染色 } for(int j=0;j<g[u].size();j++){ vis[g[u][j]][i]=1; //邻接点这种颜色不能用了 } dfs(u+1); } char s[N]; int main() { while(~scanf("%d",&n)&&n){ for(int i=0;i<n;i++){ scanf("%s",s); int len=strlen(s); g[s[0]-'A'].clear(); if(len<=2)continue; for(int j=2;j<len;j++) g[s[0]-'A'].push_back(s[j]-'A'); } memset(vis,0,sizeof(vis)); dfs(0); int ans=-1; for(int i=0;i<n;i++){ int j; for(j=0;j<n;j++)if(!vis[i][j])break; if(j>ans)ans=j; } if(ans==1)printf("1 channel needed.\n"); else printf("%d channels needed.\n",ans); } return 0; }