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;
}









posted @ 2016-07-15 00:12  HARD_UNDERSTAND  阅读(160)  评论(0编辑  收藏  举报