等和的分隔子集
晓萌希望将 1 到 N 的连续整数组成的集合划分成两个子集合,且保证每个集合的数字和是相等。例如,对于 N = 3,对应的集合 {1, 2, 3} 能被划分成 {3} 和{1, 2} 两个子集合。
这两个子集合中元素分别的和是相等的。
对于N=3,我们只有一种划分方法,而对于N=7 时,我们将有 4 种划分的方案。
输入格式
输入包括一行,仅一个整数,表示 N 的值 (1≤N≤39)。
输出格式
输出包括一行,仅一个整数,晓萌可以划分对应 N 的集合的方案的个数。当没发划分时,输出 0。
样例输入
7
样例输出
4
代码
#include<bits/stdc++.h>
using namespace std;
long long dp[50][50005];
int n,ans;
int main() {
cin>>n;
int s=(1+n)*n/2;
if(s & 1) {
cout<<0<<endl;
return 0;
}
//s & 1表示s是否为奇数,如果是奇数就肯定不能一分为二,
int ss=s/2;
dp[0][0]=1;
for(int i=1;i<ss;i++) {
dp[0][i]=0;//当背包容量为i,前零个数的方案为1
}
for(int i=1;i<=n;i++) {
for(int h=0;h<=ss;h++) {
if(h<i) dp[i][h]=dp[i-1][h];//当h<i时,意味着背包需求为h,但是我i已经大于h了,意味着肯定不能把i放进背包,所以当前情况为上一种情况
else {
dp[i][h]=dp[i-1][h]+dp[i-1][h-i];
//要么放进去要么不放进去
}
}
}
cout<<dp[n][ss]/2<<endl;//除以二是有重复的
return 0;
}
其他代码求解
#include<bits/stdc++.h>
using namespace std;
long long dp[2][50005];
int n,sum;
int main() {
cin>>n;
sum=n*(n+1)/2;
dp[0][0]=1;
if(sum%2==1) {
cout<<0<<endl;
return 0;
}
for(int i=1;i<=n;i++) {
for(int j=0;j<=sum/2;j++) {
dp[i & 1][j]=dp[!(i & 1)][j]; //把上一个状态全抄过来
}
for(int j=i;j<=sum/2;j++) {
dp[i & 1][j]=dp[!(i & 1)][j]+dp[!(i & 1)][j-i];//j先从i开始,因为我们只要到sum/2就可以
}
}
cout<<dp[n & 1][sum/2]/2<<endl;
return 0;
}
饭卡
电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额。如果购买一个商品之前,卡上的剩余金额大于或等于 5 元,就一定可以购买成功(即使购买后卡上余额为负),否则无法购买(即使金额足够)。所以大家都希望尽量使卡上的余额最少。
某天,食堂中有 n 种菜出售,每种菜可购买一次。已知每种菜的价格以及卡上的余额,问最少可使卡上的余额为多少。
输入格式
第一行为正整数 n(n≤1000) ,表示菜的数量。
第二行包括 n个正整数,表示每种菜的价格。价格不超过 5050。
第三行包括一个正整数 m(m≤1000),表示卡上的余额。
输出格式
对于每组输入,输出一行,包含一个整数,表示卡上可能的最小余额。
样例输入1
1
50
5
样例输出1
-45
样例输入2
10
1 2 3 2 1 1 2 3 2 1
50
样例输出2
32
代码
/**先对菜价进行排序,然后选出菜价最大的,从剩下的菜中选出x个使得总价格最接近
m-5,最后让m减去这个菜价和再减去菜价最大的那个即为最小余额**/
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int m,n;
int a[1101];
int dp[1101];
int solve(){
int i,j;
for(i = 0; i < n-1; i++){
for(j = m-5; j >= a[i]; j--){
dp[j] = max(dp[j],dp[j-a[i]]+a[i]);
}
}
return dp[m-5];
}
int main(){
int i;
while(cin >> n){
if(!n)break;
int i;
for(i = 0; i < n; i++)
cin >> a[i];
cin >> m;
sort(a,a+n);
memset(dp,0,sizeof(dp));
int cc = solve();
if(m<5)cout << m << endl;
else cout << m-cc-a[n-1] << endl;
}
return 0;
}
感觉有上个代码有点不舒服,这个是自己又写了一遍
#include<bits/stdc++.h>
using namespace std;
long long dp[2][50005];
int n,sum,a[1111],m;
int main() {
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
cin>>m;
if(m<5) {
cout<<m<<endl;
return 0;
}
sort(a+1,a+1+n);
dp[0][0]=0;
for(int i=1;i<n;i++) {
for(int j=0;j<=m-5;j++) {
dp[i & 1][j]=dp[!(i & 1)][j];
}
for(int j=a[i];j<=m-5;j++) {
dp[i & 1][j]=max(dp[!(i & 1)][j],dp[!(i & 1)][j-a[i]]+a[i]);
}
}
cout<<m-dp[(n-1) & 1][m-5]-a[n]<<endl;
return 0;
}
还有一种代码是别人写的,我感觉可以在里面学点东西,这里也写出来了
#include<bits/stdc++.h>
using namespace std;
long long dp[2][50005];
int n,mx,a[1111],m;
int main() {
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
cin>>m;
if(m<5) {
cout<<m<<endl;
return 0;
}
sort(a+1,a+1+n);
dp[0][0]=1;
for(int i=1;i<n;i++) {
for(int j=0;j<=m-5;j++) {
dp[i & 1][j]=dp[!(i & 1)][j];
}
for(int j=a[i];j<=m-5;j++) {
if(dp[!(i & 1)][j-a[i]]) {
dp[i & 1][j]=1;//对可行性来做
}
}
}
for(int i=m-5;i>=0;i--) {
if(dp[(n-1) & 1][i])
{
mx=max(mx,i);//一旦找到便直接break
break;
}
}
cout<<m-mx-a[n]<<endl;
return 0;
}
offer
蒜头君很早就想出国,现在他已经考完了所有需要的考试,准备了所有要准备的材料,于是,便需要去申请学校了。要申请国外的任何大学,你都要交纳一定的申请费用,这可是很惊人的。蒜头君没有多少钱,总共只攒了 n 万美元。他将在 m 个学校中选择若干的(当然要在他的经济承受范围内)。每个学校都有不同的申请费用 a(万美元),并且蒜头君估计了他得到这个学校 offer 的可能性 b。
不同学校之间是否得到 offer 不会互相影响。“I NEED A OFFER”,他大叫一声。帮帮这个可怜的人吧,帮助他计算一下,他可以收到至少一份offer的最大概率。(如果蒜头君选择了多个学校,得到任意一个学校的offer都可以)。
输入格式
第一行有两个正整数 n,m((0≤n≤10000,0≤m≤10000)。
后面的 m 行,每行都有两个数据ai(整形) bi(实型)
分别表示第 i 个学校的申请费用和可能拿到 offer 的概率。
输出格式
每组数据都对应一个输出,表示蒜头君可能得到至少一份 offer 的最大概率。用百分数表示,精确到小数点后一位。
样例输入
10 3
4 0.1
4 0.2
5 0.3
样例输出
44.0%
重点是概率的求法,
题目链接
#include<bits/stdc++.h>
using namespace std;
int a;
double b,dp[2][10005];
int main() {
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++) {
cin>>a>>b;
for(int j=0;j<=n;j++)
{
dp[i & 1][j] = dp[!(i & 1)][j];//把上一轮的情况全部抄过来
}
for(int j=a;j<=n;j++) {
dp[i & 1][j]=max(dp[i & 1][j],1.0-(1-dp[!(i & 1)][j-a])*(1-b));
//至少一份 offer 的最大概率==1.0-(这次没有得到的概率)*上次也没有得到的概率
}
}
cout<<fixed<<setprecision(1)<<dp[m & 1][n]*100<<"%"<<endl;
//cout<<fixed<<setprecision(x)<<a<<endl;
//意思是保留a小数点后的x位小数,注意fixed和setprecision是要连起来用的
//如果用printf写的话要这样写
//printf("%.1lf%%\n",dp[m&1][n]*100);
//在printf中%是有意义的。
return 0;
}