PAT 1103 Integer Factorization (30分) dfs(可行性剪枝+最有性剪枝)附数据生成器和对拍程序 Apare_xzc

2 篇文章 0 订阅
2 篇文章 1 订阅

PAT 1103 Integer Factorization (30分)

dfs+剪枝

附数据生成器和对拍程序


题目链接:

1103 Integer Factorization(30分)

题面:

PAT 1103 题面


题意:

  输入为3个正整数N, K, P, 现在要将 N 分解成 K 个数的 P 次方之和,要求这K个数组成的数列是非递增的数列。
  如果没有满足的拆分方案,输出一行“Impossilbe”, 如果有多组拆分方案,输出K个的和最大的方案,如果仍有多组方案,输出字典序最大的方案。


数据范围:

1 <= N <= 400
1 <= K <= N
2 <= P <= 7
(N,K,P 为正整数)


样例:

Sample Input 1
169 5 2
Sample Output 1
169 = 6^2 + 6^2 + 6^2 + 6^2 + 5^2
Sample Input 2
169 167 3
Sample Output 2
Impossible

分析与思路:

   因为是把正整数拆分成K个正整数的P次方之和,可能会有凑不出来的情况。那么我们如何知道凑不出来呢?
  我们先分析边界条件,如果K个数都取最小值1, 还比N大,那一定无解。但是题目条件已经保证了K<=N, 所以我们并不能直接判断出Impossible的情况。所以我们只能搜索,如果搜不到可行解,那么就是无解。

  我们不妨做一个简单的思考,如果K个数一样大,那么一定是最优解(想想为什么)

  题目要求输出K个数之和最大的可行解,如果和相同,输出字典序最大的式子。所以我们不妨按字典序从大到小进行搜索,如果sum_of_k比之前的可行解大,再更新答案

  由于是非递增的数列,所以我们在搜索的时候,只要先确定第一个数的大小。那么第一个数的上界和下界怎么确定呢?

  我们可以分析一下,如果后面的K-1个数都去最大值,即都和第一个数一样,他们的P次方和还是小于N, 这样肯定是无解,所以我们的下界down要满足 down^p * K >= N , 循环或者二分即可以找到最大的下界; 如果后面的K-1个数都取最小值,即都取1,他们的P次方之和还是小于N, 可定也无解。所以上届up要满足up^p + K - 1 <= N, 可以二分找到up的最小值。于是我们确定了第一个数的上届up和下界down.

  接下来我们就可以开始搜索了。搜索的时候,我们自然还是要从往小填数来保证字典序最大的需要,填数的上届和下界的计算方法同第一个数。

  关于剪枝, 我们可以进行可行性剪枝和最有性剪枝:

  1. 可行性剪枝:
       如果后面都放最大值都凑不够N, 就剪掉
       如果后面都放1都比N大,也剪掉
  2. 最优性剪枝:
       如果后面都放最大值,最后K个数的和还是小于当前的最大和,也剪掉

   这样搜索的效率就会比较高


我的AC代码(15ms)

#include <bits/stdc++.h>
using namespace std;
int Pow[21][8]; //Pow[x][j]中存储的是x^j 
long long a[21]; //a[i]表示Pow[i][p] 
int N,k,p; //题目输入,待分解的正整数N,要拆分成K个数的p次方之和 
int ans[405]; //搜索中用于存储拆分方案的数组 
int res[405]; //用于记录最终答案的数组 
int MaxSum; //拆分后x1,x2,x3...xk最大的和 
bool flag; //标记是否搜到了可行的拆分方案 
void dfs(int index,int pos,int remain,int tmpSum)
{ // 
	if(pos==k+1) //说明搜到了一种可行的拆分方案 
	{
		flag = true; //标记搜到了解 
		if(tmpSum>MaxSum) //更新最大的和 
		{ //因为是按字典序从大往小遍历的,所以
		//tmpSum==MaxSum并不需要更新答案 
			MaxSum = tmpSum; //更新K个数的最大和,并记录答案 
			for(int i=1;i<=k;++i) res[i] = ans[i];
		}
		return;
	}
	ans[pos] = index; //第pos个数是index 
	tmpSum += index; //当前pos个数的总和为tmpSum 
	remain -= a[index]; //remain代表N减去之前Pos个数的p次方和剩余         
	
  ///可行性剪枝: 
	if(1ll*(k-pos)*a[1]>remain) return; 
	//后面从第pos个到第K个数都放1也会使最后最后的p次方之和大于N 
	if(1ll*(k-pos)*a[index]<remain) return; //
	//后面从第pos个到第K个数都放index^p也会使最后最后的p次方之和大于N 
	
  ///最优性剪枝: 
	if(1ll*(k-pos)*index+tmpSum<MaxSum) return;
	后面从第pos个到第K个数都放index^p得到的K个数的和也小于之前搜到的最大值 
	
	for(int t=index;t>=1;--t)
	{//从index往1遍历,保证了字典序大的先搜索 
		dfs(t,pos+1,remain,tmpSum);
	}
}
int main()
{
//	freopen("in.txt","r",stdin); 
//	freopen("out2.txt","w",stdout);
	//因为p>=2,N<=400,所以填的数最大为sqrt(400)=20 
	for(int i=1;i<=20;++i) //预处理1-20的2-7次方 
		Pow[i][0] = 1;
	for(int i=1;i<=20;++i)
		for(int j=1;j<=7;++j)
			Pow[i][j] = Pow[i][j-1]*i;
	while(scanf("%d%d%d",&N,&k,&p)!=EOF)
	{  //多组输入 
//		printf("%d %d %d\n",N,k,p); //用于调试 
		map<int,int> mp; //mp中存放1-20的p次方 
		for(int i=1;i<=20;++i)
			a[i] = Pow[i][p],mp[a[i]]++;
		if(N%k==0&&mp.count(N/k)) //注意判断键的存在性要用count
	    //打codeforces的时候吃过两次亏,用[]访问直接MLE 
		{ //K个数都相同和一定最大
			int y = N/k; //存在x使得x^p * K = N 
			int x = lower_bound(a,a+21,y)-a; //二分查找确定x的值
			//输出答案,行末无空格 
			printf("%d = %d^%d",N,x,p);
			for(int i=2;i<=k;++i) 
				printf(" + %d^%d",x,p);
			puts("");
			continue;
		}
		MaxSum = 0;
		//up和down是第一位的上界和下界 
		int up = 0; //a[up] <= N-K+1的最大值 
		int down = 0; //a[down]*K>=n;的最小值 
		for(int i=1;i<=20;++i)
			if(a[i]<=N-k+1) up = i;	
		for(int i=1;i<=20;++i)
		{ 
			if(a[i]*k>=N)
			{ 
				down = i;break;
			} 
		} 
		flag = false;
		for(int st=up;st>=down;--st)
		{ 
			dfs(st,1,N,0);
		}	
		if(!flag) puts("Impossible");
		else
		{
			printf("%d = %d^%d",N,res[1],p); 
			for(int i=2;i<=k;++i)
				printf(" + %d^%d",res[i],p);
			puts("");
		}
	}
	return 0;
} 

在这里插入图片描述


数据生成器:

  由于这道题N,K,P范围都不大,所以我们可以穷尽所有的合法输入。只要保证:

1 <= N <= 400
1 <= K <= N
2 <= P <= 7
(N,K,P 为正整数)

即可,所以数据生成也非常好写,只需要三个for加文件输出即可

数据生成器代码

CreateDate.cpp

//Create Date.cpp
//Author xzc
//2020.1.31 
#include <bits/stdc++.h>
using namespace std;
int main()
{
	freopen("in.txt","w",stdout);
	for(int N=1;N<=400;++N)
	{
		for(int k=1;k<=N;++k)
		{
			for(int p=2;p<=7;++p)
				cout<<N<<" "<<k<<" "<<p<<endl;
		}
	}
	return 0;
}

运行完CreateDate.cpp之后,我们得到一个较大的输入文件in.txt, 可以用notepad++或者其它文本编辑器打开来查看
in
in2


生成标准答案:

  因为这个题也没什么其它的更稳妥,更暴力的方法生成答案,所以,我们可以用已经AC了的正确代码来计算出 (400+1)*400/2*6 = 481200组N,P,K的输入的解
  我用我的AC代码,改成多组输入,然后用freopen()输入输出重定向,得到了所有输入的解,用时119秒
All Test
可以得到一个更大的输出文件,由于为了对排方便,我在每个样例的答案前一行都输出了N,K,P的值。输出文件out.txt的内容展示如下:
out1
out2


对拍程序:judge.cpp

  比较输出文件和标准答案的异同,如果不同,得到相应的输入和错误输出,并且统计出错的个数。
  因为我进行了比较好的剪枝,所以程序跑得飞快,119秒就跑完了481200组输入的数据,但是你的程序如果没有剪枝,或者cin,cout,或者滥用STL,或者该二分查找的非要for遍历等等,可能根本不能在可以接受的时间内跑完所有的合法输入,所以,我们人性化一点儿,没有直接比较两个文件到文件尾,我们设置了比较的行数。

#include <bits/stdc++.h>
using namespace std;
string ask1,res1,ask2,res2;
int main()
{
	ifstream Ans("out.txt"); //标程生成的标准答案 
	ifstream Out("MyOut.txt"); //你的输出 
//	freopen("Judge_result.txt","w",stdout);  
	int cnt = 0;
	for(int i=1;i<=962400;i+=2)
	{
		getline(Ans,ask1);
		getline(Ans,res1);
		getline(Out,ask2);
		getline(Out,res2);
		if(res1!=res2)
		{
			cout<<ask1<<endl;
			cout<<"ans is: "<<res1<<endl;
			cout<<"Myout is: "<<res2<<endl;
			cout<<endl;
			++cnt;
		}
	}
	Ans.close();
	Out.close();
	if(cnt==0) cout<<"AC"<<endl;
	else cout<<"cnt = "<<cnt<<endl;
	
	return 0;	
} 

在这里插入图片描述
评测结果显示:
resWa
在这里插入图片描述


AC愉快~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Nilmtk是一款用于非侵入式家庭用电能监测的Python工具包。它可以获取家庭电力数据,对其进行预处理、解和预测,以实现家庭用电的非侵入式监测,从而更加了解家庭用电情况,并作出相应的节能措施。 在Nilmtk中,对于家庭用电数据解,常用的方法是非负矩阵解(Non-Negative Matrix Factorization,NMF)。它将家庭用电数据解为不同的负载,使得每个负载的用电数据都可以被拆为对应的负载源和时间序列。在预测方面,常用的方法是F1-score,即平衡精度和召回率。 对于Redd数据集,Nilmtk可以对其进行解和预测。该数据集包含了来自6个家庭的电子设备的非侵入式用电数据,以及这些设备的标签。利用Nilmtk对这些数据进行解,将电子设备的用电数据解为不同的负载,以实现对不同设备的监测和预测。同时,通过使用Nilmtk的预测功能,可以建立相应的监测模型,实现对家庭用电的预测,为节能提供技术支持。 此外,Nilmtk也提供了可视化的展示功能,可以将家庭用电数据以图表形式呈现,让用户更加直观地了解家庭用电情况,从而采取相应的措施。因此,Nilmtk是一款非常有用的家庭用电监测工具,可以帮助用户更加了解家庭用电情况,提高用电意识,减少不必要的能源浪费,并推进绿色低碳生活的实现。 ### 回答2: Nilmtk是一种用于非侵入式负载解和能源消耗预测的工具。它可以帮助用户析家庭能源使用情况,比较不同家庭、不同时间段和不同设备的能源消耗情况,并能够为未来的能源消耗提供预测。 在使用Nilmtk对redd(REFIT电力数据集)进行解和预测时,可以采用如下步骤: 1. 数据准备:首先需要获取REDD数据集。此数据集包括6套不同家庭、13个过程和近200个电力数据读数,用于在不同家庭中对电力需求进行类和预测。然后,将数据集导入到Nilmtk中,对数据进行准备。 2. 数据预处理:在数据准备好后,需要对数据进行预处理。这包括数据的清洗、去重和规范化。这将确保数据的准确性和一致性。 3. 负载解:在预处理完数据后,可以开始进行负载解(Load Disaggregation)。这将对数据进行解,以确定电力数据中每个设备的使用情况。Nilmtk可以帮助自动检测和抽取有意义的特征,从而识别和离出每个设备的能源消耗信息。 4. 能源消耗预测:解完成后,需要对设备的能源消耗进行预测。Nilmtk可以使用多种模型和算法来进行预测析,包括统计学方法、机器学习方法等。用户可以选择最适合自己的方法来实现负载预测。 总之,Nilmtk是一种功能强大、灵活和易于使用的工具,可以对REDD数据集进行解和预测,帮助用户更好地理解自己家庭的能源消耗情况,从而实现优化能源使用的目的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值