2017-7-17/18 背包dp cf round 417 div2

  poj3624

 0-1背包 裸 

 1 #include<iostream>
 2 #include<cstdio>
 3 
 4 using namespace std;
 5 const int maxn=15000;
 6 int w[maxn];
 7 int v[maxn];
 8 int cost;
 9 int main()
10 {
11     int n,m;
12     scanf("%d%d",&n,&cost);
13     for(int i=0;i<n;i++)
14     {
15         scanf("%d%d",&w[i],&v[i]);
16     }
17     int dp[maxn];
18     memset(dp,0,sizeof(dp));
19     for(int i=0;i<n;i++)
20     {
21         for(int j=cost;j>=w[i];j--)
22         {
23             dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
24         }
25     }
26     cout<<dp[cost]<<endl;
27 }

   poj3628

2^20 枚举即可

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<cstring>
 4 #include<vector>
 5 
 6 using namespace std;
 7 
 8 int a[22];
 9 int n,m;
10 
11 
12 int cal(int x)
13 {
14     int i=0;
15     int sum=0;
16     while(x)
17     {
18         if(x&1) sum+=a[i];
19         x>>=1;
20         i++;
21     }
22     return sum;
23 }
24 
25 
26 int main()
27 {
28     scanf("%d%d",&n,&m);
29     for(int i=0;i<n;i++)
30         scanf("%d",&a[i]);
31 
32     int num=2e9;
33 
34     for(int i=0;i<=(1<<n)-1;i++)
35     {
36         int d=cal(i);
37         if(d<num&&d>=m) num=d;
38     }
39     printf("%d
",num-m);
40 }

方法2:  0-1背包 用所有cow的高度和作为背包体积V,最后找到最小的但是大于m的即可,背包体积从小到大枚举找到ans

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<vector>

using namespace std;

int dp[20000005];
int a[22];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int sum=0;
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]),sum+=a[i];
    dp[0]=0;
    for(int i=0;i<n;i++)
    {
        for(int j=sum;j>=a[i];j--)
            dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
    }
    for(int i=0;i<=sum;i++)
    {
        if(dp[i]>=m) {printf("%d
",dp[i]-m);break;}
    }
    
}

 poj 1837 

题意:背包变形,在一个天平上有一些挂钩 一些砝码,将所有砝码挂上去,问使得天平平衡的方案数是多少?

思路:类似背包 这里使用7500作平衡点,另外dp[i][j]表示前i个砝码都放上去,平衡度为j时的方案数目,引入平衡度的概念。那么dp[i][j]=sigma(dp[i-1][j-c[k]*w[i]]),0<=k<n, ans=dp[m][7500];

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

int n,m;

int w[50];
int c[50];
int dp[22][15010];

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=m;i++) scanf("%d",&w[i]);
    memset(dp,0,sizeof(dp));
    dp[0][7500]=1;

    for(int i=1;i<=m;i++)
    {
        for(int j=0;j<=15000;j++)
        {
            for(int k=1;k<=n;k++)
            {
                dp[i][j]+=dp[i-1][j-c[k]*w[i]];
                //dp[i][j+c[k]*w[i]]+=dp[i-1][j];
            }
        }
    }


    printf("%d
",dp[m][7500]);
}

 hdu 2548 饭卡

思路:如果余额小于5元,就直接输出余额,如果余额大于五元就直接留出来买最贵的那个,然后去掉五元之外的钱,用来做0-1背包,买其他的物品

#include<bits/stdc++.h>

using namespace std;

int w[1010];
int dp[50000+100];
int t;
int cost;
int n;

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0;i<n;i++) scanf("%d",&w[i]);
        scanf("%d",&cost);
        if(cost<5) {printf("%d
",cost);continue;}
        sort(w,w+n);
        cost-=5;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n-1;i++)
        {
            for(int j=cost;j>=w[i];j--)
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
        }
        printf("%d
",cost-dp[cost]+5-w[n-1]);
    }
    return 0;
}

  poj 3903  nlog(n)求最长递增子序列LIS

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 
 6 using namespace std;
 7 
 8 int n;
 9 const int maxn=100010;
10 const int inf=0x7fffffff;
11 int dp[maxn];
12 int a[maxn];
13 
14 int main()
15 {
16     while(~scanf("%d",&n))
17     {
18         for(int i=0;i<n;i++) scanf("%d",&a[i]);
19 
20         fill(dp,dp+n,inf);
21 
22         for(int i=0;i<n;i++)
23             *lower_bound(dp,dp+n,a[i])=a[i];
24         int ans=lower_bound(dp,dp+n,inf)-dp;
25         printf("%d
",ans);
26     }
27 }

 hdu 1203 0-1背包变形

题意:qn要去申请n所学校,但是只有cost前,每所学校都有申请费用以及录取概率,求qn有学上的最大概率

 1 #include<bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 double dp[10010];
 6 int w[10010];
 7 double p[10010];
 8 int n,cost;
 9 
10 double cal(double x,double y)
11 {
12     double c=1.0-(1-x)*(1-y);
13     return c;
14 }
15 
16 int main()
17 {
18     while(~scanf("%d%d",&cost,&n)&&(cost||n))
19     {
20         for(int i=0;i<n;i++)
21             scanf("%d%lf",&w[i],&p[i]);
22         memset(dp,0,sizeof(dp));
23         for(int i=0;i<n;i++)
24         {
25             for(int j=cost;j>=w[i];j--)
26                 dp[j]=max(dp[j],cal(dp[j-w[i]],p[i]));
27         }
28         printf("%.1f%%
",dp[cost]*100);
29     }
30 }

 hdu 2955 

题目大意:rob计划要抢银行,一共有n个银行,给定逃跑概率上限值lim(安全概率),对每个银行都有一个可以抢的钱数以及被抓的概率,问你在保证安全情况下,最多抢到多少钱?

思路:这题从被抓概率考虑,我们肯定要求最小的,这样就违背了背包的原理,我们反过来,从逃跑概率出发,也就是说求逃跑概率最大值满足条件,我们把抢到的钱看做体积,逃跑概率看做价值,这样背包之后,再从末尾往前查满足条件的即可,需要注意的是边界要进行初始化,0的时候 就是不抢 概率为1 ,同时还要注意一定要恰好装满背包 需要重复查一次

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int t;
 5 int n;
 6 int cost;
 7 double lim;
 8 int w[110];
 9 double p[110];
10 
11 double dp[10010];
12 
13 double cal(double x,double y)
14 {
15     return x*y;
16 }
17 int main()
18 {
19     scanf("%d",&t);
20     while(t--)
21     {
22         cost=0;
23         scanf("%lf%d",&lim,&n);
24         lim=1-lim;
25         for(int i=0;i<n;i++)
26             scanf("%d%lf",&w[i],&p[i]),cost+=w[i],p[i]=1-p[i];
27         memset(dp,0,sizeof(dp));
28         dp[0]=1;
29 
30         for(int i=0;i<n;i++)
31         {
32             for(int j=cost;j>=w[i];j--)
33             {
34                 dp[j]=max(dp[j],cal(dp[j-w[i]],p[i]));
35             }
36         }
37 
38         int ans;
39         for(int i=cost;i>=0;i--)
40         {
41             if(dp[i]>=lim) {
42                 ans=i;
43                 break;
44             }
45         }
46         int pos=0;
47         for(int i=ans;i>=1;i--)
48         {
49             if(dp[i]!=dp[i-1]){
50                 pos=i;
51                 break;
52             }
53         }
54 
55         printf("%d
",pos);
56     }
57 }

poj  1948   Triangular Pastures 子集和问题

题目大意:有n个木棒,给出每个木棒的长度,将所有三角形都用上,求所能构成的三角形的最大面积,最后结果乘以100   向下取整,若不能构成三角形 输出-1;

题目思路,dp[i][j][k]表示前i个木棒能不能拼成两条边的长度分别为j和k,那么第三条边就可以减法求出来

  dp[i][j][k]=(dp[i-1][j][k]|dp[i-1][j-w[i]][k]|dp[i-1][j][k-w[i]]); 由于内存限制,优化掉第一层,滚动数组

 1 #include<iostream>
 2 #include<stdio.h>
 3 #include<cstring>
 4 #include<cmath>
 5 
 6 using namespace std;
 7 
 8 int n;
 9 int w[50];
10 bool dp[1610][1610];
11 
12 double cal(double p,int a,int b,int c)
13 {
14     return sqrt(double(p*1.0*(p-a)*(p-b)*(p-c)));
15 }
16 
17 
18 
19 int main()
20 {
21     scanf("%d",&n);
22     int sum=0;
23     for(int i=0;i<n;i++)
24         scanf("%d",&w[i]),sum+=w[i];
25     memset(dp,0,sizeof(dp));
26     dp[w[0]][0]=dp[0][w[0]]=1;
27 
28     double ans=0;
29 
30     int flag=0;
31 
32     for(int i=1;i<n;i++)
33     {
34         for(int j=sum;j>=0;j--)
35         {
36             for(int k=sum;k>=0;k--)
37             {
38                 if(j-w[i]>=0) dp[j][k]|=dp[j-w[i]][k];
39                 if(k-w[i]>=0) dp[j][k]|=dp[j][k-w[i]];
40                 int c=sum-j-k;
41                 if(dp[j][k]&&j+k>c&&j+c>k&&k+c>j){
42                     ans=max(ans,cal(sum/2.0,j,k,c));
43                     flag=1;
44                 }
45             }
46         }
47     }
48     if(flag)
49         printf("%d
",int(ans*100));
50     else puts("-1");
51 
52 }

题目大意:有一个十字路口的交通灯,给出各种左转,右转,直行,人行横道,判断是否会撞到人行横道

思路:分别对直行、左转、右转进行判断即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int l[4];
 5 int s[4];
 6 int r[4];
 7 int p[4];
 8 
 9 int main()
10 {
11     for(int i=0;i<4;i++)
12         cin>>l[i]>>s[i]>>r[i]>>p[i];
13     for(int i=0;i<4;i++)
14     {
15         if((l[i]||r[i]||s[i])&&p[i]){
16             cout<<"YES";return 0;
17         }
18     }
19     for(int i=0;i<4;i++)
20     {
21         if(r[i]&&p[(i+1)%4]){
22             cout<<"YES";return 0;
23         }
24     }
25     for(int i=0;i<4;i++)
26     {
27         if(l[i]&&p[(i+4-1)%4]){
28             cout<<"YES";return 0;
29         }
30     }
31     for(int i=0;i<4;i++)
32     {
33         if(s[i]&&p[(i+2)%4])
34         {
35             cout<<"YES";return 0;
36         }
37     }
38     cout<<"NO";
39     return 0;
40 }

B

题目大意:一个矩阵,最左右两列表示楼梯,中间的m列表示灯,走一步需要1min,每次关完本层的灯,再去关上层的灯,从左下角出发,求关完所有的灯需要的时间

思路:

预处理一下,对每一层从左边或者右边出发 需要花费多少时间,进行二进制枚举每一层从左边走还是右边走,用pre保存下一层从左边走的还是从右边走的,然后进行枚举求最小值,

注意两个地方:

一是注意预处理一下需要走上第几层,二是不需要向上走时,就不用再动了,二进制枚举右移次数有限制

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 int n,m;
 5 int ma[20][110];
 6 int l[20];
 7 int r[20];
 8 const int inf=0x7fffffff;
 9 
10 
11 int main()
12 {
13     memset(l,0,sizeof(l));
14     memset(r,0,sizeof(r));
15     char c;
16     cin>>n>>m;
17     for(int i=0;i<n;i++)
18     {
19         for(int j=0;j<m+2;j++)
20         {
21             cin>>c;
22             ma[i][j]=c-'0';
23         }
24     }
25     for(int i=n-1;i>=0;i--)
26     {
27         for(int j=0;j<m+2;j++)
28         {
29             if(ma[i][j]==1){
30                 l[i]=max(l[i],j);
31                 r[i]=max(r[i],m+1-j);
32             }
33         }
34     }
35 
36     int flag=n-1;
37     for(int i=0;i<n;i++)
38     {
39         if(l[i]) {flag=i;break;}
40     }
41 
42     int ans=inf;
43     for(int i=0;i<=(1<<(n-1))-1;i++)
44     {
45         int p=i;
46         int sum=0;
47         int pre=0;
48         for(int j=n-2,k=0;j>=0&&k<n-1-flag;j--,k++)
49         {
50             if(p&1){
51                 if(pre){
52                     sum+=r[j+1]+r[j];
53                     pre=1;
54                 }
55                 else {
56                     sum+=(m+1-l[j+1])+r[j];
57                     pre=1;
58                 }
59             }
60             else{
61                 if(pre){
62                     sum+=(m+1-r[j+1])+l[j];
63                     pre=0;
64                 }
65                 else {
66                     sum+=l[j+1]+l[j];
67                     pre=0;
68                 }
69             }
70             p>>=1;
71         }
72         sum+=l[n-1];
73         sum+=n-1-flag;
74 
75         ans=min(ans,sum);
76     }
77     cout<<ans;
78 }

  C 

简单二分,

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 int n,cost;
 7 struct node
 8 {
 9     int v;
10     int ord;
11     LL cal;
12 }a[100000+100];
13 const int inf=0x7fffffff;
14 LL num=inf;
15 
16 int cmp(node u,node v)
17 {
18     if(u.cal<v.cal) return 1;
19     else return 0;
20 }
21 
22 int check(int x)
23 {
24     LL sum=0;
25     for(int i=1;i<=n;i++)
26     {
27         a[i].cal=a[i].ord*1LL*x+a[i].v;
28     }
29     sort(a+1,a+n+1,cmp);
30     for(int i=1;i<=x;i++)
31     {
32         sum+=a[i].cal;
33         if(sum>cost*1LL) return 0;
34     }
35 
36 
37 
38     num=sum;
39 
40     return 1;
41 }
42 
43 
44 int main()
45 {
46     scanf("%d%d",&n,&cost);
47     for(int i=1;i<=n;i++)
48     {
49         scanf("%d",&a[i].v);
50         a[i].ord=i;
51     }
52     int l=0,r=n;
53     int ans=0;
54     while(l<=r)
55     {
56 
57         int mid=(l+r)/2;
58         if(check(mid)) {
59             ans=mid;
60             l=mid+1;
61         }
62         else {
63             r=mid-1;
64         }
65     }
66     printf("%d %I64d
",ans,num);
67 }
原文地址:https://www.cnblogs.com/hellohacker/p/7196177.html