dfs剪枝:洛谷P2809 hzwer爱折纸

传送门
在这里插入图片描述

解析

dfs暴力枚举即可
这题的重点是如何剪枝
不难发现,随着不断处理,纸条只会越来越短,且所有数字总加和不变
我一开始想到了2个条件:
1.当前长度比理想纸条小,return;
2.总加和与理想纸条不等,直接输出N
但是这样仍是O(n!)的级别,得到了80分
后来又想到:
反转之后对后一半的与反转前对前一半的折叠是等效的,所以只需要递归一半的长度即可
从而通过本题

代码

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <queue>
#include <string>
#include<map>
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a));
using namespace std;
const int N=50;
const int M=1e9;
int n,m,mod;
int a[N],b[N];
int save[N][N],save2[N][N];
int flag;
void memory(int l){
	for(int i=0;i<=a[0];i++) save[l][i]=a[i];
}
void restore(int l){
	for(int i=0;i<=save[l][0];i++) a[i]=save[l][i];
}
void fold(int p){
	memory(a[0]);
	int x=0,l=a[0];
	if(p<=a[0]/2){
		for(int i=save[l][0];i>=p+p+1;i--) a[++x]=save[l][i];
		for(int i=1;i<=p;i++) a[++x]=save[l][i]+save[l][p+p-i+1];
		a[0]-=p;
	}
	else{
		for(int i=1;i<=2*p-a[0];i++) a[++x]=save[l][i];
		for(int i=2*p-save[l][0]+1;i<=p;i++) a[++x]=save[l][i]+save[l][save[l][0]-(i-(2*p-save[l][0]))+1];
		a[0]-=(a[0]-p);
	}
	return;
}
void memory2(int l){
	for(int i=0;i<=a[0];i++) save2[l][i]=a[i];
}
void restore2(int l){
	for(int i=0;i<=save2[l][0];i++) a[i]=save2[l][i];
}
void fanzhuan(){
	memory2(a[0]);
	for(int i=1;i<=a[0];i++) a[i]=save2[a[0]][save2[a[0]][0]-i+1];	
}
bool judge(){
	for(int i=0;i<=b[0];i++){
		if(a[i]!=b[i]) return false;
	}
	return true;
}
void print(){
	for(int i=1;i<=a[0];i++) printf("%d ",a[i]);
	printf("\n");
}
bool jd(){
	int pd[N]={},ok[N]={};
	for(int i=1;i<=a[0];i++) pd[i]=a[i];
	sort(pd+1,pd+1+a[0]);
	for(int i=1;i<=b[0];i++) ok[i]=b[i];
	sort(ok+1,ok+1+b[0]);
//	for(int i=1;i<=b[0];i++){
//		printf("b=%d a=%d\n",ok[i],pd[i]);
//	}
	for(int i=1;i<=b[0];i++){
		if(ok[i]<pd[i]) return false;
	}
	return true;
}
void dfs(){
//	print();
	if(flag) return;
	if(a[0]<b[0]) return;
//	if(!jd()) return;
	int l=a[0];
	if(a[0]==b[0]){
		if(judge()){
			printf("S\n");flag=1;return;
		}
		fanzhuan();
		if(judge()){
			printf("S\n");flag=1;
		}
		restore2(l);
		return;
	}
	for(int i=1;i<=a[0]/2;i++){
		fold(i);
		dfs();
		if(flag) return;
		restore(l);
	}
	fanzhuan();
	for(int i=1;i<=a[0]/2;i++){
		fold(i);
		dfs();
		if(flag) return;
		restore(l);
	}
	restore2(l);
	return;
}
int main(){
	while(scanf("%d",&n)==1){
		flag=0;
		int tot=0;
		mem(a,0);mem(b,0);
		for(int i=1;i<=n;i++) mem(save[i],0);
		for(int i=1;i<=n;i++){
			scanf("%d",&a[i]);tot+=a[i];
		}
		a[0]=n;
		scanf("%d",&m);
		for(int i=1;i<=m;i++){
			scanf("%d",&b[i]);
			tot-=b[i];
		}
		b[0]=m;
		if(m>n||tot!=0){
			printf("N\n");continue;
		}
		dfs();
		if(!flag) printf("N\n");
	}
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值