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;
}
}