E-智乃的凑数题

题目链接:https://ac.nowcoder.com/acm/contest/103957/E

题意:

给定一个数组,分别选出一些数使得其乘积之和是否等于x

思路:

将ai x bi 求和拆分成 ai之和 x bi之和
发现如果要让x等于这个两个数的乘积,那么这两个数分别是x的因子
将选取数看成01背包
记dp数组:dp[i,j]=1表示能到达这个状态,i表示行之和,j表示列之和
dp[i,j]=dp[i-a[k][j]
dp[i,j]=dp[i][j-a[k]]
如果dp[i,j]已经存在,那么就必须要continue

状态转移
对于单个方案的方法使用回溯,用分别记录pre分别记录走x还是走y

int arr[105];
int x,n;
int ok;

void solve(){
	int m;cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>arr[i];
	vector<vector<int>>dp(200,vector<int>(200,0));
	vector<vector<int>>pre(200,vector<int>(200,0));
	dp[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int x=100;x>=0;x--){
			for(int y=100;y>=0;y--){
				if(dp[x][y])continue;
				if(x-arr[i]>=0&&dp[x-arr[i]][y]){
					dp[x][y]|=dp[x-arr[i]][y];
					pre[x][y]=arr[i];
				}
				else{
				 if(y-arr[i]>=0&&dp[x][y-arr[i]]){
					dp[x][y]|=dp[x][y-arr[i]];
					pre[x][y]=-arr[i];
				}
				}
			}
		}
	}
	
	rep(i,1,m){
		cin>>x;
		int ok=0;
		for(int j=1;j*j<=x;j++){
			if(x%j==0){
				vector<int>res1,res2;
				int a=j,b=x/j;
				while((a!=0||b!=0)&&dp[a][b]){
					ok=1;	
					if(pre[a][b]>0){
						res1.pb(pre[a][b]);
						a-=pre[a][b];
					}else{
						res2.pb(-pre[a][b]);
						b+=pre[a][b];
					}
				}
				if(ok){
					cout<<"Yes"<<endl;
					cout<<res1.size()<<' '<<res2.size()<<endl;
					for(int k=0;k<res1.size();k++){
						cout<<res1[k]<<' ';
					}cout<<endl;
					for(int k=0;k<res2.size();k++){
						cout<<res2[k]<<' ';
					}cout<<endl;
					break;
				}
			}
		}
		if(!ok)cout<<"No"<<endl;
	}

}
posted @ 2025-03-29 13:22  Marinaco  阅读(11)  评论(0)    收藏  举报
//雪花飘落效果