回路计数
由题干可知排列组合暴力求解的话时间复杂度是20!,大概是10的18次方的计算量,而普通oj一般1s内算10的八次方左右,时间太长了等不起,借鉴y总的根据时间复杂度查算法的表得知n<30时,本题要么dfs剪枝要么状压DP。剪枝太难想了,状态压缩的话第一维是21个教学楼走过的状态,第二维是当前位置,可以解。
//用状压dp,当前状态为i,当前在第j号教学楼
#include<bits/stdc++.h>
using namespace std;
const int M=(1<<21),N=21;
typedef long long LL;
LL f[M][N];
bool g[N][N];
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
//将教学楼的编号变为0-20 方便位运算
int main()
{
for(int i=1;i<=N;i++)
for(int j=i;j<=N;j++)
if(gcd(i,j)==1)
g[i-1][j-1]=g[j-1][i-1]=true;
//计算状态为f[21个1][1~20]的所有状态数,1与任何数都互质
f[1][0]=1;
//从小到大枚举每一个状态,对这个状态走到下一个状态
for(int i=1;i<=M-1;i++)
{
//枚举现在在哪个教学楼
for(int j=0;j<21;j++)
{
if((i>>j)&1)
{
//枚举上一步是从哪来的,没走过且能连通
for(int k=0;k<21;k++)
{
if((i-(1<<j))>>k&1 &&g[j][k])
{
f[i][j]+=f[i-(1<<j)][k];
}
}
}
}
}
LL sum=0;
for(int i=1;i<21;i++)
{
sum+=f[M-1][i];
}
cout<<sum;
}
砝码称重
#include<bits/stdc++.h>
using namespace std;
//dp,只用前n个砝码能否称出w的重量
const int N=110,M=1e5+10;
typedef long long LL;
int f[N][M];
int n,sum=0;
LL w[N];
int main()
{
memset(f,0,sizeof f);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>w[i];
sum+=w[i];
}
f[0][0]=1;
//w能被称出有三种情况
//1.w在n-1的时候就可以称出
//2.w+w[n]在n-1的时候能被称出
//3.w-w[n]在n-1的时候能被称出
//枚举只用前n个砝码
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
if(f[i-1][j]||f[i-1][j+w[i]]||f[i-1][abs(j-w[i])])f[i][j]=1;
}
}
//统计有多少个重量可以被枚举出来
int cnt=0;
for(int i=1;i<=sum;i++)
{
if(f[n][i])
{
cnt++;
}
}
cout<<cnt;
return 0;
}