挑战程序设计竞赛2.3节习题选解

poj2385

题目链接:https://vjudge.net/problem/POJ-2385

设dp[i][j]表示经过i秒移动了j次能接到的最多苹果数,则dp[i][j]=max(dp[i][j],dp[i-k][j-1]+sum[i][j%2+1]-sum[i-k][j%2+1]);(第i-k秒从一棵树移动到另一棵树,之后在另一棵树下不动)tree[i][1]=1表示第i秒第一棵树掉下来一个苹果,tree[i][2]=1同理,tree[i][1]或tree[i][2]=0表示当前的树不掉苹果,sum[i][1]和sum[i][2]分别为tree[i][1]和tree[i][2]的前缀和,用这个求出在一段时间内在某一棵树底下不移动时,能拿到多少苹果

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=100+10;
 6 const int maxw=30+10;
 7 int main()
 8 {
 9     int tree[maxn][3],sum[maxn][3];
10     int dp[maxn][maxw];
11     int i,j,k,t,n,w,s,ans,x;
12     cin>>n>>w;
13     memset(tree,0,sizeof(tree));memset(sum,0,sizeof(sum));
14     for (i=1;i<=n;i++)
15       {
16           cin>>x;
17           if (x==1) tree[i][1]=1;else tree[i][2]=1;
18       }
19     for (i=1;i<=n;i++)
20       {
21           sum[i][1]=sum[i-1][1]+tree[i][1];
22           sum[i][2]=sum[i-1][2]+tree[i][2];
23       }
24     memset(dp,0,sizeof(dp));
25     for (i=1;i<=n;i++) dp[i][0]=sum[i][1];
26     for (i=1;i<=n;i++)
27       for (j=1;j<=min(i,w);j++)
28         for (k=1;k<=i-j+1;k++)
29           dp[i][j]=max(dp[i][j],dp[i-k][j-1]+sum[i][j%2+1]-sum[i-k][j%2+1]);
30     ans=0;
31     for (i=0;i<=w;i++) ans=max(ans,dp[n][i]);
32     cout<<ans<<endl;
33     return 0;
34 } 
poj2385

 

poj3616

题目链接:https://vjudge.net/problem/POJ-3616

LIS问题的变形。用f[i]表示到第i个区间内最高的价值,dp[i]=max(dp[i],dp[j]+a[j].v),若j的右边端点<=i的左边端点

当然,要先按开始时间(左端点)排个序

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxm=100+10;
struct node
{
    int l,r,v;
}a[maxm];
bool cmp(node p,node q)
{
    return p.l<q.l;
}
int main()
{
    int i,j,k,n,m,rr,ans;
    int dp[maxm];
    cin>>n>>m>>rr;
    for (i=1;i<=m;i++) cin>>a[i].l>>a[i].r>>a[i].v;
    sort(a+1,a+m+1,cmp);
    memset(dp,0,sizeof(dp));
    for (i=1;i<=m;i++) dp[i]=a[i].v;
    for (i=1;i<=m;i++)
      for (j=1;j<=i-1;j++)
        if (a[j].r+rr<=a[i].l) dp[i]=max(dp[i],dp[j]+a[i].v);
    ans=0;
    for (i=1;i<=m;i++) ans=max(ans,dp[i]);
    cout<<ans<<endl;
    return 0;
}
poj3616

 

poj3280题解:https://www.cnblogs.com/edmunds/p/12783673.html

poj1742题解:https://www.cnblogs.com/edmunds/p/12442642.html

 

poj3046:

题目链接:https://vjudge.net/problem/POJ-3046

2.3节多重集组合数的练习题,基本和例题一样。设dp[i][j]表示选到第i种,一共选出了j个的方案数

可以很容易写出一个转移方程:dp[i][j]=dp[i-1][j]+dp[i-1][j-1]+...+dp[i-1][j-min(a[i],j)]

但是这样会时间复杂度过高。注意这一段 dp[i-1][j-1]+...+dp[i-1][j-min(a[i],j)]应该和dp[i][j-1]有关系。下面分情况讨论:

1) 若a[i]>=j,dp[i][j]=dp[i-1][j]+(dp[i-1][j-1]+...+dp[i-1][0])=dp[i-1][j]+dp[i][j-1];

2) 若a[i]<j,dp[i][j]=dp[i-1][j]+(dp[i-1][j-1]+...+dp[i-1][j-a[i]])=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-a[i]-1]

这样递推的时间复杂度就降到O(T*A)

注意取模。我也不知道为啥一开始算出来是负数,然后在第二个递推式子里加上一个mod再模mod就对了......按理说应该不可能会算出来负数的啊??

还要注意边界:dp[i][0]=1;i=0-t

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int mod=1e6;
const int maxt=100+10;
const int maxa=1000+10;

int dp[maxt][maxa],num[maxt];
int t,a,s,b,i,j,ans,k,x;

int main(){
    //freopen("poj3046.txt","r",stdin);
    cin>>t>>a>>s>>b;
    memset(num,0,sizeof(num));
    for (i=1;i<=a;i++){
        cin>>x;num[x]++;
    }
    memset(dp,0,sizeof(dp));
    for (i=0;i<=t;i++) dp[i][0]=1;
    for (i=1;i<=t;i++)
      for (j=1;j<=a;j++){
          if (num[i]>=j) dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
          else dp[i][j]=(dp[i-1][j]+dp[i][j-1]-dp[i-1][j-num[i]-1]+mod)%mod;
      }
    ans=0;
    for (i=s;i<=b;i++) ans=(ans+dp[t][i])%mod;
    cout<<ans<<endl;
    //fclose(stdin);
    return 0;
}
poj3046

 另外,如果空间不够(比如poj1742那样),可以用滚动数组优化,因为注意到dp[i][j]只和dp[i][...]和dp[i-1][...]有关

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxt=3000+10;
 6 const int mod=1000000;
 7 int main()
 8 {
 9     int n,i,j,k,t,b,ans,s,m;
10     int a[maxt],f[2][maxt];
11     cin>>t>>n>>s>>b;
12     memset(a,0,sizeof(a));
13     for (i=1;i<=n;i++) {
14           cin>>k;a[k]++;
15        } 
16     ans=0;
17     memset(f,0,sizeof(f));
18     for (i=0;i<=1;i++) f[i][0]=1;
19     for (i=1;i<=t;i++)
20           for (j=1;j<=b;j++){             
21               if (j>a[i]) f[i%2][j]=(f[(i-1)%2][j]+f[i%2][j-1]-f[(i-1)%2][j-1-a[i]]+mod)%mod;  //滚动数组 
22             else f[i%2][j]=(f[(i-1)%2][j]+f[i%2][j-1])%mod;
23         }
24     for (i=s;i<=b;i++) ans=(ans+f[t%2][i])%mod;
25     cout<<ans<<endl;
26     return 0;
27 }
poj3046,滚动数组

 

poj1065

题目链接:https://vjudge.net/problem/POJ-1065

套路题。对一个端点排序后,由dilworth定理对另一个端点求最长严格下降子序列即可

 1 #include<iostream>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int maxn=500+10;
 6 struct node{int l,w;};int f[maxn];
 7 node a[maxn];
 8 bool cmp(node p,node q){
 9     if (p.l==q.l) return p.w<q.w;
10     else return p.l<q.l;
11 }
12 int main()
13 {
14     int i,j,k,ans,t,n;
15     //freopen("poj1065.txt","r",stdin);
16     cin>>t;
17     while (t--){
18           cin>>n;
19           memset(f,0,sizeof(f));
20           for (i=1;i<=n;i++) cin>>a[i].l>>a[i].w;
21           sort(a+1,a+n+1,cmp);
22           f[1]=1;
23         for (i=2;i<=n;i++){
24           f[i]=1; 
25           for (j=1;j<=i-1;j++)
26             if (a[j].w>a[i].w) f[i]=max(f[i],f[j]+1);
27         }
28         ans=0;
29         for (i=1;i<=n;i++) ans=max(ans,f[i]);
30         cout<<ans<<endl;
31        } 
32     //fclose(stdin);
33     return 0;
34 }
poj1065

 

poj1631以前做过了......还是求LIS的问题

poj3666题解:https://www.cnblogs.com/edmunds/p/12306923.html

poj2392题解:https://www.cnblogs.com/edmunds/p/12783660.html

poj2184题解:https://www.cnblogs.com/edmunds/p/12783708.html

 

posted @ 2020-04-08 14:25  coastal_taipan  阅读(138)  评论(0编辑  收藏  举报