Brute Force美学

文章目录

  • 搜索
      • P1706 全排列问题
      • P1157 组合的输出
      • P1582 倒水
      • P3958 奶酪
      • Hzwer 破冰派对
      • P1433 吃奶酪
      • P1171 售货员的难题
      • P1074 靶型数独
      • P1120 小木棍
      • P1443 马的遍历
      • P1025 数的划分
      • P2386 放苹果
      • P1019 单词接龙
      • P1141 01迷宫
      • P1118 Backward Digit Su
      • P1087 FBI树
      • P1030 求先序遍历
      • P3183 食物链
      • P2723 Humble Number
  • (待处理)分块
      • P2574 XOR的艺术
      • P1972 HH的项链
      • P2709 小B的询问
      • P4145 花神游历各国
      • P2023 维护序列
      • Hzwer 分块7题
      • P4168 蒲公英

搜索

P1706 全排列问题

递归,建立搜索树
另外有一个骚操作
printf("%5d",x);
d前边加数字可控制间距

#include
#include
#include
int const maxn=23,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int n,use[maxn],vis[maxn];
inline void dfs(int pos)
{
	if(pos==n+1)
	{
		for(int i=1;i<=n;i++)
			printf("%5d",use[i]);
		puts("");
		return;
	}
	for(int i=1;i<=n;i++)
		if(!vis[i])
			use[pos]=i,vis[i]=true,dfs(pos+1),vis[i]=false;
}
int main()
{
	scanf("%d",&n);
	dfs(1);
	return 0;
}

P1157 组合的输出

跟上一题唯一的区别是满足递增关系…

#include
#include
#include
int const maxn=23,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int n,m;
int vis[maxn],use[maxn]; 
inline void dfs(int pos,int last)
{
	if(pos==m+1)
	{
		for(int i=1;i

P1582 倒水

这题不会做,看了题解真想扇自己一巴掌,所以说做题还是自己想比较好
易发现,水瓶里的水只能是2的幂,所以2^n个初始的瓶子可以合为1个瓶子
我们发现这根本就是二进制加法…
于是我们把问题转化一下,把n用二进制表示,看看可以n的初始瓶子最少能表示为多少个2的幂的和
比如13=2^0 + 2^2 + 2^3,最少可以合并到3个瓶子
然后贪心的从低位加瓶子即可
正确性可以证明

#include
#include
#include
#define int long long
int const maxn=23,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int n,k;
inline int lowbit(int x)
{
	return x&-x;
}
int work(int x)
{
	int num=0;
	while(x)
	{
		num++;
		x-=lowbit(x);
	}
	return num;
}	
inline int get(int x)
{
	int tot=0;
	while(x)
	{
		tot+=x&1;
		x>>=1;
	}
	return tot;
}
inline void solve()
{
	int ans=0;
	while(get(n)>k)
	{
		int pos=lowbit(n);
		ans+=pos;
		n+=pos;
	}
	printf("%lld",ans);
}
signed main()
{
	scanf("%lld%lld",&n,&k);
	solve();
	return 0;
}

P3958 奶酪

搜索水题,不过有坑点…
1.不要回溯时清掉访问标记,这又不是求最优解,有解的话是一定能搜到的
2.注意溢出的问题
3.注意只要洞跟下底面接触就能走 (而不是跟坐标原点求距离公式)

#include
#include
#include
#include
int const maxn=1101,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int t,n,h,r;
int ans,vis[maxn];
int x[maxn],y[maxn],z[maxn],a[maxn];
inline int cmp(int x,int y)
{
    return z[x]=h)
    {
        ans=true;
        return;
    }
    vis[now]=true;
    for(int j=1;j<=n;j++)
    {
        int i=a[j];
        if(vis[i]||now==i)
            continue;
        long long dis=distant(x[now],y[now],z[now],x[i],y[i],z[i]);
        if(dis<=(long long)4*pow(r))
            dfs(i);
    }
//    vis[now]=false;
    return;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        memset(x,0,sizeof(x));
        memset(y,0,sizeof(y));
        memset(z,0,sizeof(z));
        memset(vis,0,sizeof(vis));
        scanf("%d%d%d",&n,&h,&r);
        ans=0;
        for(int i=1;i<=n;i++)
            scanf("%d%d%d",&x[i],&y[i],&z[i]),a[i]=i;
        std::sort(a+1,a+1+n,cmp);
        for(int j=1;j<=n;j++)
        {
            int i=a[j];
            if(z[i]<=r)
                dfs(i); 
        }
        if(ans)
            puts("Yes");
        else
            puts("No");
    }
    return 0;
}

Hzwer 破冰派对

我真是太弱了…一开始还想建图…
建啥图啊,直接枚举每个点不就行了…

#include
#include
#include
int const maxn=1111,maxm=599500,maxe=501000,inf=0x1f1f1f1f;
int t,n,m,ans;
int map[maxn][maxn],ton[maxn],don[maxn];
inline bool check1(int u,int num)
{
    if(!num)
        return true;
    for(int i=1;i<=num;i++)
        if(!map[ton[i]][u])
            return false;
    return true;
}
inline bool check2(int u,int num)
{
    if(!num)
        return true;
    for(int i=1;i<=num;i++)
        if(map[don[i]][u])
            return false;
    return true;
}
inline void dfs(int pos,int numt,int numd)
{
    if(pos==n+1)
    {
        if(numt&&numd)
            ans++;
        return;
    }
    if(check1(pos,numt))
        ton[numt+1]=pos,dfs(pos+1,numt+1,numd),ton[numt+1]=0;
    if(check2(pos,numd))
        don[numd+1]=pos,dfs(pos+1,numt,numd+1),don[numd+1]=0;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        ans=0;
        memset(map,0,sizeof(map)); 
        scanf("%d%d",&n,&m);
        for(int u,v,i=1;i<=m;i++)
            scanf("%d%d",&u,&v),
            map[u][v]=map[v][u]=true;
        dfs(1,0,0);
        printf("%d\n",ans);
    }
}

P1433 吃奶酪

经典的TSP问题!
一般解法

  • dfs+最优性剪枝
#include
#include
#include
#include
int const maxn=23,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int n;double x[maxn],y[maxn];bool vis[maxn];
double ans=inf;
inline double pow(double x)
{
	return x*x;
}
inline double distant(int last,int now)
{
	return sqrt(pow(x[last]-x[now])+pow(y[last]-y[now]));
}
inline void dfs(int u,int pos,double len)
{
	if(len>=ans)
		return;
	if(pos==n)
	{
		ans=std::min(ans,len);
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[i])
			continue;
		vis[i]=true;
		dfs(i,pos+1,len+distant(u,i));
		vis[i]=false;
	}
	return;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lf%lf",&x[i],&y[i]);
	dfs(0,0,0);
	printf("%.2lf",ans);
	return 0;
}
  • 状压dp 详见售货员的难题

P1171 售货员的难题

状压dp
被无情卡常被迫吸了口氧…
f[i][j]表示i状态下走到j

#include
#include
#include
int const maxn=23,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int n,map[maxn][maxn],mx;
int Pos[maxn];
int f[maxm][maxn];
inline void rin()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%d",&map[i][j]);
    return;	
}
inline void pretreatment()
{
    memset(f,0x1f,sizeof(f)); 
    mx=(1<

P1074 靶型数独

难受啊!!!
打的表开到13没打大括号结果全完了,查了一上午…
另外我还真是navie…
一行一行搜就是了,非要向右向下同时搜…
该题关键在于优化搜索顺序
先填0少的行,优化搜索状态

#include
#include
#include
#include
int  const maxn=13,maxm=1148576,maxe=501000,inf=0x1f1f1f1f;
int  Zero[maxn],Map[maxn][maxn],lit[maxn][maxn],a[maxn],row[maxn][maxn],line[maxn][maxn];
int  mx,ans=-1;
int  cmp(int  x,int  y)
{
	return Zero[x]

P1120 小木棍

66分代码,无fuck说
剪枝如下

  • 优化搜索顺序,长度从小向大枚举,直接找第一个合法解
  • 当某一长度的木棍不可行时,跳过同长度的木棍,可用桶排实现
  • 每次找最不灵活的,因为他最容易不合法,如是,直接挂掉就好
#include
#include
#include
#include
int const maxn=72,inf=0x3f3f3f3f;
double const esp=1e-5;
int n,L[maxn];
int sum,mx,mn,ans;
int len,vis[maxn],ton[maxn],cnt;
void dfs(register int num,register int lftV)
{ 
    if(!num)
    {
        printf("%d",len);
        exit(0);
    }
    if(!lftV)
    {
        register int next=-1;
        for(register int i=cnt;i>=1;i--)
            if(ton[L[i]])
            {
                next=i;
        		break;
            }
        if(next==-1&&num!=1)
            return;
        ton[L[next]]--,dfs(num-1,len-L[next]),ton[L[next]]++;
        return;
    }
    for(register int i=cnt;i>=1;i--)
        if(ton[L[i]]&&lftV>=L[i])
        {
            ton[L[i]]--,dfs(num,lftV-L[i]),ton[L[i]]++;
            if(L[i]==lftV || len==lftV)
            	return;
        }
    return;
}
int main()
{
    scanf("%d",&n);
    for(register int a,i=1;i<=n;i++)
    {
        scanf("%d",&a);
        if(a>50)
            continue;
        ton[a]++;
        sum+=a,mx=std::max(mx,a),mn=std::min(mn,a);
    }
    for(register int i=mn;i<=mx;i++)
    	if(ton[i])
    		L[++cnt]=i;
    for(len=mx;len<=sum/2;len++)
    {
        double num=(double)sum/len;
        if(num!=(int)num)
            continue;
        dfs(num,len);
    }
    printf("%d",sum);
    return 0;
} 

P1443 马的遍历

这题显然是bfs,然而dfs直接爆搜可以过9个点…
DFS

#include
#include
#include 
int const maxn=423,inf=0x1f1f1f1f;
int const dx[8]={2,1,2,1,-2,-1,-2,-1};
int const dy[8]={1,2,-1,-2,1,2,-1,-2};
int n,m,sx,sy,map[maxn][maxn],vis[maxn][maxn];
void dfs(int x,int y,int step)
{
	map[x][y]=std::min(map[x][y],step);
	for(int p=0;p<8;p++)
	{
		int vx=x+dx[p],vy=y+dy[p];
		if(vx<1||vx>n||vy<1||vy>m)
			continue;
		if(map[vx][vy]>step+1)
			dfs(vx,vy,step+1);
	}
}
int main()
{
	scanf("%d%d%d%d",&n,&m,&sx,&sy);
	memset(map,0x1f,sizeof(map));
	dfs(sx,sy,0);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
			printf("%-5d",map[i][j]==inf?-1:map[i][j]);
		puts("");
	}
	return 0;
}

BFS

#include
#include
#include
#include
int const maxn=432,inf=0x1f1f1f1f;
int const dx[8]={2,1,2,1,-2,-1,-2,-1};
int const dy[8]={1,2,-1,-2,1,2,-1,-2};
struct node
{
    int x,y;
    node(int x=0,int y=0):
        x(x),y(y){}
};
int n,m,sx,sy,map[maxn][maxn];
inline void bfs()
{
    std::queueq;
    q.push(node(sx,sy));
    map[sx][sy]=0;
    while(!q.empty())
    {
        node u=q.front();
        q.pop();
        for(int p=0;p<8;p++)
        {
            node v=(node){u.x+dx[p],u.y+dy[p]};
            if(v.x<1||v.x>n||v.y<1||v.y>m)
                continue;
            if(map[v.x][v.y]>map[u.x][u.y]+1)
                q.push(v),map[v.x][v.y]=map[u.x][u.y]+1;
        }
    }
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&sx,&sy);
    memset(map,0x1f,sizeof(map));
    bfs();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            printf("%-5d",map[i][j]==inf?-1:map[i][j]);
        puts("");
    }
    return 0;
}

P1025 数的划分

不重复那就不下降呗…

#include
#include
int const maxn=111,inf=0x1f1f1f1f;
int ans,n,k;
void dfs(int pos,int now,int lftV)
{
	if(pos==k&&!lftV) {ans++;return;}
	if(pos==k||lftV<0) return;
	for(int i=now;i<=lftV;i++)
		dfs(pos+1,i,lftV-i);
}
int main()
{
	scanf("%d%d",&n,&k);
	dfs(0,1,n);
	printf("%d",ans);
	return 0;
}

P2386 放苹果

可以不放那就从0开始呗…

#include
#include
int const maxn=111,inf=0x1f1f1f1f;
int ans,n,k;
void dfs(int pos,int now,int lftV)
{
    if(pos==k&&!lftV) {ans++;return;}
    if(pos==k||lftV<0) return;
    for(int i=now;i<=lftV;i++)
        dfs(pos+1,i,lftV-i);
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        ans=0;
        scanf("%d%d",&n,&k);
        dfs(0,0,n);
        printf("%d\n",ans);
    }
    return 0;
}

P1019 单词接龙

恶心
爆搜即可

#include
#include
#include
#include
int const maxn=111;
int n,bns,ton[maxn];
std::string s[maxn];
void dfs(int now,int ans)
{
    bns=std::max(bns,ans);
    for(int i=1;i<=n;i++)
    {
        if(!ton[i])
            continue;
        for(int j=s[now].size()-1;j>=0;j--)
        {
            int ky=0;
        	int flag=false;
            for(int kx=j;kx>s[i],ton[i]+=2;
    std::cin>>s[n+1];
    for(int i=1;i<=n;i++)
        if(s[i][0]==s[n+1][0]) 
    		ton[i]--,dfs(i,s[i].size()),ton[i]++;
    printf("%d",bns);
    return 0;
}

DFS求四联通的模板题

P1141 01迷宫

#include
#include
#include
int const maxn=1111;
int const dx[4]={0,0,1,-1};
int const dy[4]={1,-1,0,0};
int n,m;
int fa[maxn*maxn],num[maxn*maxn],vis[maxn*maxn];
char Map[maxn][maxn];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int dfs(int x,int y)
{
    if(fa[x*n+y])
        return find(x*n+y);
    fa[x*n+y]=x*n+y,num[x*n+y]=1;
    for(int p=0;p<4;p++)
    {	
        int vx=x+dx[p],vy=y+dy[p];
        if(vx>=1&&vx<=n&&vy>=1&&vy<=n&&Map[x][y]!=Map[vx][vy])
        {
            dfs(vx,vy);
            int fu=find(x*n+y),fv=find(vx*n+vy);
            if(fu!=fv)
                fa[fv]=fu,num[fu]+=num[fv];
        }
    }
    return find(x*n+y);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",Map[i]+1);
    for(int sx,sy,i=1;i<=m;i++)
    {
        scanf("%d%d",&sx,&sy);
        printf("%d\n",num[dfs(sx,sy)]);
    }
    return 0;
}

P1118 Backward Digit Su

杨辉三角形+枚举全排列

第一行的n个数分别为a,b,c,…,对于sum有一定的规律
如果n为4,那么sum是a+3b+3c+d。
如果n为5,那么sum是a+4b+6c+4d+e。
如果n为6,那么sum是a+5b+10c+10d+5e+f

不难发现,各项的系数就是杨辉三角

另外,求某一行的杨辉三角有窍门

  • 利用对称性
  • 利用组合数公式 C n , m = C n − 1 , m ∗ n − m + 1 m C_{n,m}=C_{n-1,m}*\frac{n-m+1}{m} Cn,m=Cn1,mmnm+1

再就是注意杨辉三角中第n行的第m个数表示的是 C n − 1 , m − 1 C_{n-1,m-1} Cn1,m1

#include
#include
#include 
int const maxn=17;
int n,sum,vis[maxn],use[maxn],f[maxn];
void dfs(int pos,int tot)
{
	if(pos==n+1)
	{
		if(tot!=sum)
			return;
		for(int i=1;i<=n;i++)
			printf("%d ",use[i]);
		exit(0);
	}
	if(tot>sum)
		return;
	for(int i=1;i<=n;i++)
		if(!vis[i])
			use[pos]=i,vis[i]=true,dfs(pos+1,tot+i*f[pos-1]),vis[i]=false,use[pos]=0;
	return;
	
}
int main()
{
	scanf("%d%d",&n,&sum);
	f[0]=1,f[n-1]=1;
	for(int i=1;i*2

P1087 FBI树

后序遍历,尽展递归魅力

#include
#include
int const maxn=13;
int n;
char Map[maxn];
void find(int l,int r)
{
	if(l!=r)
	{
		int mid=l+r>>1;
		find(l,mid),find(mid+1,r);
	}
	int flag0=false,flag1=false;
	for(int i=l;i<=r;i++)
	{
		if(Map[i]=='0')
			flag0=true;
		if(Map[i]=='1')
			flag1=true;
	}
	if(flag0&&!flag1)
		printf("B");
	if(!flag0&&flag1)
		printf("I");
	if(flag0&&flag1)
		printf("F");
	return;
}
int main()
{
	scanf("%d",&n);
	scanf("%s",Map+1);
	find(1,1<

P1030 求先序遍历

  • 后序遍历的结尾字母作为当前子树的根
  • 把中序遍历按照根分为左右子树
  • 把后序遍历按照中序遍历分为左右子树
  • 递归子树

substr(x,y)拷贝从x开始的长度为y的子串

#include
#include
#include
std::string sec,lst;
void before(std::string mid,std::string aft)
{
    if(mid.size()>0)
    {
        char ch=aft[aft.size()-1];
        std::cout<>sec>>lst;
    before(sec,lst);
    return 0;
}

P3183 食物链

裸的爆搜

#include
#include
#include 
int const maxn=100110,maxm=201210;
struct E
{
	int to,next;
	E(int to=0,int next=0):
		to(to),next(next){}
}e[maxm];
int head[maxn],cnt;
int n,m,ans;
int f[maxn],ind[maxn],oud[maxn];
inline void add(int u,int v)
{
	e[++cnt]=(E){v,head[u]};
	head[u]=cnt;
}
void dfs(int u)
{
	if(!oud[u])
	{
		ans++;
		return;
	}
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		dfs(v);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int u,v,i=1;i<=m;i++)
		scanf("%d%d",&u,&v),ind[v]++,oud[u]++,add(u,v);
	for(int i=1;i<=n;i++)
		if(!ind[i]&&oud[i])
			dfs(i);
	printf("%d",ans);
	return 0;
}

花了1min记忆化一下,AC

#include
#include
#include 
int const maxn=100110,maxm=201210;
struct E
{
	int to,next;
	E(int to=0,int next=0):
		to(to),next(next){}
}e[maxm];
int head[maxn],cnt;
int n,m,ans;
int f[maxn],ind[maxn],oud[maxn];
inline void add(int u,int v)
{
	e[++cnt]=(E){v,head[u]};
	head[u]=cnt;
}
int dfs(int u)
{
	if(f[u])
		return f[u];
	if(!oud[u])
		return f[u]=1;
	for(int i=head[u];i;i=e[i].next)
	{
		int v=e[i].to;
		f[u]+=dfs(v);
	}
	return f[u];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int u,v,i=1;i<=m;i++)
		scanf("%d%d",&u,&v),ind[v]++,oud[u]++,add(u,v);
	for(int i=1;i<=n;i++)
		if(!ind[i]&&oud[i])
			ans+=dfs(i);
	printf("%d",ans);
	return 0;
}

P2723 Humble Number

挺好的题

易知一个丑数一定是由某比他小的丑数乘上s集合中的数得来的
这样我们可以分析出一个性质1
对于集合中的一个数x来说,假如他乘上丑数i得到丑数i+1,那么他只有乘上更大的丑数才能得到比i+1更大的丑数

这样我们就可以对于每个数i<=n,都枚举集合s中的元素
记录一下当前元素需要乘第几小的丑数才能比i-1大
这个寻找第一个大于i-1的数的过程可以用二分实现
然而我写跪了
对所有元素取min作为第i小的丑数

#include
#include
#include
#include
int const maxn=100110,maxm=111,inf=0x7f7f7f7f;
int n,k;
long long f[maxn];
int s[maxm],a[maxm];
/*
inline int find(int id,int check)
{
    int ans=0,l=s[id],r=k;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(a[id]*f[mid]>check)
        {
            ans=mid;
            r=mid-1;
        }
        else
            l=mid+1;
    }
    return ans;
}
*/
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
  //  std::sort(a+1,a+1+n);
    f[0]=1;
//	for(int i=1;i<=k;i++)
//		f[i]=9999;
    for(int i=1;i<=k;i++)
    {
        long long minn=inf;
        for(int j=1;j<=n;j++)
        {
//            s[j]=find(j,f[i-1]);
            while(a[j]*f[s[j]]<=f[i-1])
                s[j]++;
              //根据性质1,我们只需要从上一次更新开始找即可
            minn=std::min(minn,a[j]*f[s[j]]);
        }
        f[i]=minn;
    }
    printf("%lld",f[k]);
    return 0;
}

(待处理)分块

P2574 XOR的艺术

P1972 HH的项链

P2709 小B的询问

P4145 花神游历各国

P2023 维护序列

Hzwer 分块7题

P4168 蒲公英

你可能感兴趣的:(Brute,Force,分块,算法)