题目: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
#include
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;ians)ans=t;
for(int i=k+1;isum){ //剪枝,只有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
题目: poj 1416
题意:
整数划分问题,要求把所给整数划分,使得划分得到的的数的和尽量接近一个目标数字。
分析:
枚举划分的个数,dfs分配划分的位置,然后把划分的数加起来求和即可。
我是用一个string保存划分位置,最后再按照string保存的划分位置再求和。
剪枝的话应该是每划分一次就把所得到的和,与未划分的数加起来,然后与已经得到最优解比较,判断是否剪枝即可。
我的代码没优化,懒得改了QAQ。
#include
#include
#include
#include
#include
using namespace std;
int n,ans,total,ansnum,len;
vectorvec;
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;is;
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;i1)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
#include
#include
using namespace std;
const int N=11;
typedef pairpii;
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
#include
#include
using namespace std;
const int N=30;
vectorg[N]; //存边
int n;
bool vis[N][N];
void dfs(int u)
{
if(u==n)return;
int i;
for(i=0;ians)ans=j;
}
if(ans==1)printf("1 channel needed.\n");
else printf("%d channels needed.\n",ans);
}
return 0;
}