0/1背包问题:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n=5,c=10,w[5]={2,2,6,5,4},v[5]={6,3,5,4,6};
int k,kk,i;
int maxvalue=0;
for(k=0;k<(1<<5);k++){
kk=k;
int tempweight=0,tempvalue=0;
for(i=0;i<5;i++){
if(kk%2==1){
tempweight+=w[i];
tempvalue+=v[i];
}
kk/=2;
}
if(tempweight<=c &&tempvalue>=maxvalue)
maxvalue=tempvalue;
}
cout<<maxvalue;
return 0;
}
个人理解的关键代码:
比如填写dp[2][3]的时候,如果装物品2(重量是3),那么就相当于把物品1装到了(总容量-3)的背包中,然后总价值加上此价值
不装物品2的时候,相当于只把物品1撞到里面 dp[2][3]=dp[1][3]
其中可以优化的地方:滚动数组
int dp[1001]
itn ans(){
memset(dp,0,sizeof(dp));
for(int i=1;i<=N;i++)
for(int j=V;j>=bone[i].vol;j--)
dp[j]=max(dp[j],dp[j-bone[i].vol]+bone[i].val);
return dp[V];
空间复杂度从NV->V 有时候数据太大,不用滚动数组,会MLE
有缺点,获得不到中间路径
例题1:P2392 kkksc03考前临时抱佛脚
输入输出样例
输入
1 2 1 3
5
4 3
6
2 4 3
输出
20
思路:
在这里sum是所有题目的总时间 也相当于01背包中的总体积V,01背包中的价值也是这里每一个题目的时间,此题用到了滚动数组优化
#include<bits/stdc++.h>
using namespace std;
int a[5],i,j,k,sum,t,homework[21],dp[2501];
int main(){
for(i=1;i<=4;i++)
cin>>a[i];
for(i=1;i<=4;i++){
sum=0;
for(j=1;j<=a[i];j++)
{cin>>homework[j];//输入
sum+=homework[j];}//总时间累加
for(j=1;j<=a[i];j++)
for(k=sum/2;k>=homework[j];k--)//只要是总和的一半
dp[k]=max(dp[k],dp[k-homework[j]]+homework[j]);//01背包
t+=sum-dp[sum/2];//累加为另一个脑子
for(j=1;j<=sum/2;j++)
dp[j]=0;//清零
}
cout<<t;//输出
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int len[4];
int sub[30];
int f[21][1201];
int main(){
ios::sync_with_stdio(false);
for(int i=0;i<4;i++) cin>>len[i];
int tot=0;
for(int i=0;i<4;i++){
int v=0;
for(int j=1;j<=len[i];j++) cin>>sub[j],v+=sub[j];
sort(sub,sub+len[i]);
int t1=0;
for(int j=1;j<=len[i];j++)
for(int k=0;k<=v/2;k++){
f[j][k]=f[j-1][k];
if(k>=sub[j])f[j][k]=max(f[j][k],f[j-1][k-sub[j]]+sub[j]);
t1=max(f[j][k],t1);
}
tot+=max(t1,v-t1);
}
cout<<tot<<endl;
return 0;
}
补充一个学习大佬的搜索办法
#include<bits/stdc++.h>
using namespace std;
int Left,Right,minn,ans;
int s[5];
int a[21][5];
void search(int x,int y){
if(x>s[y]){
minn=min(minn,max(Left,Right));
return;
}
Left+=a[x][y];
search(x+1,y);
Left-=a[x][y];
Right+=a[x][y];
search(x+1,y);
Right-=a[x][y];//毫无技巧的搜索回溯
}
int main(){
cin>>s[1]>>s[2]>>s[3]>>s[4];
for(int i=1;i<=4;i++){//减少码量
Left=Right=0;
minn=19260817;
for(int j=1;j<=s[i];j++)
cin>>a[j][i];
search(1,i);
ans+=minn;
}
cout<<ans;
return 0;
}
例题2:任务分配
现有n个任务,要交给A和B完成。每个任务给A或给B完成,所需的时间分别为ai和bi。问他们完成所有的任务至少要多少时间。
Input
第一行一个正整数n,表示有n个任务。 接下来有n行,每行两个正整数ai,bi。Output
一个数,他们完成所有的任务至少要的时间。Samples
Input Copy
3
5 10
6 11
7 12
Output
12
大佬代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 700;
const ll mod = 1e9+7;
#define mst(x, a) memset( x,a,sizeof(x) )
#define rep(i, a, b) for(int i=(a);i<=(b);++i)
#define dep(i, a, b) for(int i=(a);i>=(b);--i)
ll read() {
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') {
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void out(ll x) {
int stackk[40];
if (x < 0) {
putchar('-');
x = -x;
}
if (!x) {
putchar('0');
return;
}
int top = 0;
while (x) stackk[++top] = x % 10, x /= 10;
while (top) putchar(stackk[top--] + '0');
}
ll qpow(ll a,ll b) {
ll ans=1;
while(b) {
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int n,x[maxn],y[maxn],dp[666][6666];
int main() {
n=read();
rep(i,1,n) x[i] = read(),y[i] =read();
//dp[i][j] 表示前i个任务 a花费j时间 b花费dp[i][j]时间的最小价值
//dp[i][j] = 给A做 dp[i-1][]
int s=0;
for(int i=1 ; i<=n ; i++) {
s+=x[i];
for(int j=s ; j>=0 ; j--) {
dp[i][j] = dp[i-1][j] + y[i];
if(j>=x[i]) dp[i][j] = min(dp[i][j],dp[i-1][j-x[i]]);
}
}
int ans = inf;
rep(i,0,s) ans = min(ans,max(i,dp[n][i]));
out(ans);
return 0;
}
/*
*/
例题3:2019
一天,小赵遇到一个只含有2,0,1,9四种字符的字符串,他想知道里面有多少个2019,自己能得多少个不同的2019。只要有一个位置不同就算一个新的2019,如,20199就有2个2019,9012有0个2019。
小赵开心的把今天的事告诉小静,小静听完问:“程序竞赛更好玩吗?”小赵回答:“它不是好不好玩的问题,他是……算了,我把这道题明天讲给你听,你就知道好不好玩了。”小赵为了证明打程序设计竞赛好玩,他找到了你,希望你能做出这道题,看看小赵能得到几个2019,因为结果可能很大,所以结果请对1e9+7进行取模。
Samples
Input
2019119
220109
Output
4
2
思路:
这算是一个递推的解法,跟大佬学的。dp怎么做不太会,有大佬路过求教。
以220109为例:
循环这些数后,有两个2 所以x1变成2,预估有两个2019,然后循环第三个是0所以x2变成2,循环第四个是1,x3变成2(一直累加又因为x3本身是0),循环第五个是0,那么x2=x2+x1=4 也就是此时有可能的情况为4种(开头两个2 加上后面两个0组成可能4种情况)但是最终答案只看x4的。
循环最后一个是9,所以x4=x4+x3=2 所以前面x2更新为4并没有影响最后的结果 因为0后面没有1进行连续了。
code:
#include<iostream>
#include<string.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
char s[100005];
int main(){
ll i,j,x1,x2,x3,x4,len,ans;
//string s;
while(scanf("%s",s)!=EOF){
x1=x2=x3=x4=0;
//len=s.length();
len=strlen(s);
for(i=0;i<len;i++){
if(s[i]=='2'){
x1++;
}
else if(s[i]=='0'){
x2+=x1;
x2=x2%mod;
}
else if(s[i]=='1'){
x3+=x2;
x3=x3%mod;
}
else if(s[i]=='9'){
x4+=x3;
x4=x4%mod;
}
}
ans=x4%mod;
printf("%lld\n",ans);
}
return 0;
}