数塔问题
for(i=0;i<N;i++)
{
dp[N-1][i]=a[N-1][i];
}
for(i=N-2;i>=0;i--)
{
for(j=0;j<=i;j++)
{
dp[i][j]=max(dp[i+1][j],dp[i+1][j+1])+a[i][j];
}
}
printf("%d\n",dp[0][0]);
母牛的故事
5以后每年都是上一年加上前三年的dp,原来有的加上三年前有的,三年前有的都会在这一年生一个母牛。
for(i=1;i<5;i++) dp[i]=i;
for(i=5;i<55;i++)
{
dp[i]=dp[i-1]+dp[i-3];
}
}
超级楼梯
每多上一层楼就等于上一级和上上级,从上级走一步到这级,从上上级走两步到这级。到这级的方法有两种,相加就好。
dp[1]=0;dp[2]=1;dp[3]=2;
for(i=4;i<41;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
Bone Collector
01背包问题,t种物品,总重最大volu,求最大价值。注意j=0开始!!!还有j>=w[i],其他值记得赋值。
memset(dp,0,sizeof(dp));
for(i=1;i<=t;i++)
{
for(j=0;j<=volu;j++)
{
if(j>=w[i])
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
else dp[i][j]=dp[i-1][j];
}
}
printf("%d\n",dp[t][volu]);
饭卡
贪心:为了更划算,留下最大的最后买。 剩余的菜用sum-5购买最大价值的。转换为01背包问题,重量和价值都为a[i],最大承重是sum-5。
#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
int n,i,j,yu,money;
while(scanf("%d",&n)!=EOF&&n)
{
int a[n+5];
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
scanf("%d",&yu);
int dp[n+5][yu];
memset(dp,0,sizeof(dp));
if(yu<5)
{
printf("%d\n",yu);continue;
}
sort(a+1,a+n+1);
money=yu-5;
for(i=1;i<n;i++)
{
for(j=1;j<=money;j++)
{
if(j>=a[i])
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i]]+a[i]);
}
else dp[i][j]=dp[i-1][j];
}
}
printf("%d\n",yu-dp[n-1][money]-a[n]);
}
return 0;
}
选修课
多重背包问题,注意j逆取。
memset(dp,0,sizeof(dp));
dp[0]=1;
for(i=1;i<=k;i++)
{
for(j=n;j>=a[i];j--)
{
for(l=1;l<=b[i];l++)
{
if(j>=a[i]*l) dp[j]+=dp[j-a[i]*l];
}
}
}
printf("%d\n",dp[n]);
Sumsets
1、2的倍数为组合数,问一个数有多少种组合方式?
当这个数为奇数时,即上一个数每个数加一,方法数不变。
为偶数时,即上一个数的方法每个加一,如果加数里没有1,即对n/2的每一个加数式乘以2,总类数为a[n/2];
所以总的种类数为:a[n]=a[n-2]+a[n/2];加一 或者乘二;
a[1]=1;a[2]=2;
for(i=3;i<1000001;i++)
{
if(i%2==0) a[i]=a[i-2]+a[i/2];
else a[i]=a[i-1];
a[i]=a[i]%1000000000;
}
命运
一定要注意INF值,特别注意,不能写为-1000,或更小
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=-100;
int dp[25][1005];
int a[25][1005];
int x,y;
int main()
{
int n,ans;
scanf("%d",&n);
while(n--)
{
memset(dp, inf, sizeof(dp));
scanf("%d%d",&x,&y);
for(int i=1;i<=x;i++)
{
for(int j=1;j<=y;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=0;i<=x;i++)
{
for(int j=0;j<=y;j++)
{
if(i==0||j==0) dp[i][j]=0;
ans=inf;
ans=max(dp[i-1][j],dp[i][j-1]);
for(int k=1;k<j;k++)
{
if(j%k==0) ans=max(ans,dp[i][k]);
}
dp[i][j]=ans+a[i][j];
}
}
printf("%d\n",dp[x][y]);
}
return 0;
}
二进制处理多重背包问题
Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.
You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.
Input
The input contains several test cases. The first line of each test case contains two integers n(1 ≤ n ≤ 100),m(m ≤ 100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1 ≤ Ai ≤ 100000,1 ≤ Ci ≤ 1000). The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
3 10 1 2 4 2 1 1 2 5 1 4 2 1 0 0
Sample Output
8 4
数组a是每个商品的价值,数组c是每个商品的数量。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
int a[105],b[105],c[100005],dp[100005];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
{
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=0;i<n;i++)
scanf("%d",&b[i]);
int k=0;
for(int i=0;i<n;i++)
{
for(int j=1;j<=b[i];j<<=1)
{
c[k++]=a[i]*j;
b[i]-=j;
}
if(b[i]>0)
{
c[k++]=a[i]*b[i];
}
}
memset(dp,0,sizeof(dp));
for(int i=0;i<k;i++)
{
for(int j=m;j>=c[i];j--)
{
dp[j]=max(dp[j],dp[j-c[i]]+c[i]);
}
}
int cnt=0;
for(int i=1;i<=m;i++)
{
if(dp[i]==i){
cnt++;
}
}
printf("%d\n",cnt);
}
return 0;
}
另一种方法:判断是否能组成这个数,增加一个一维数组num[ j ],记录到达j元时i种钱用的次数。每多一种硬币种类,重新赋值num[ j ] 为0。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long ll;
int a[105],b[105];
int dp[100005],num[100005];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF&&(n||m))
{
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
num[j]=0;
for(int j=a[i];j<=m;j++)
{
if(!dp[j]&&dp[j-a[i]]&&num[j-a[i]]<b[i])
{
dp[j]=1;
num[j]=num[j-a[i]]+1;
}
}
}
int cnt=0;
for(int i=1;i<=m;i++)
{
if(dp[i])
cnt++;
//printf("%d ",dp[i]);
}
printf("%d\n",cnt);
}
}
Problem Description
Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.
Input
Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, ..., n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line ``1 0 1 2 0 0''. The maximum total number of marbles will be 20000.
The last line of the input file will be ``0 0 0 0 0 0''; do not process this line.
Output
For each colletcion, output ``Collection #k:'', where k is the number of the test case, and then either ``Can be divided.'' or ``Can't be divided.''.
Output a blank line after each test case.
Sample Input
1 0 1 2 0 0 1 0 0 0 1 1 0 0 0 0 0 0
Sample Output
Collection #1: Can't be divided. Collection #2: Can be divided.
给出六个数字,每个商品的价值就是位置坐标大小,每个商品的数量是该位置的值。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=20005;
const int maxm=120010;
int dp[maxm];
int b[maxn];
int main()
{
int a[8];
int cas=1;
while(1)
{
int sum=0,k=1;
memset(a,0,sizeof(a));
for(int i=1;i<=6;i++)
{
scanf("%d",&a[i]);
sum+=a[i]*i;
for(int j=1;j<=a[i];j<<=1)
{
b[k++]=i*j;
a[i]-=j;
}
if(a[i]>0)
b[k++]=a[i]*i;
}
if(sum==0) return 0;
printf("Collection #%d:\n",cas++);
if(sum%2==1)
{
printf("Can't be divided.\n\n");
continue;
}
else
{
sum/=2;
memset(dp,0,sizeof(dp));
for(int i=1;i<k;i++)
{
for(int j=sum;j>=b[i];j--)
{
dp[j]=max(dp[j],dp[j-b[i]]+b[i]);
}
}
if(dp[sum]==sum) printf("Can be divided.\n");
else printf("Can't be divided.\n");
printf("\n");
}
}
return 0;
}