学习总结7

P1120 小木棍
题意:将给出的一组数全部利用并组合,构成多个特定数,问这个特定的数最小是多少。
输入 #1复制
9
5 2 1 5 2 1 5 2 1
输出 #1复制
6
思路:
读完该题后第一个想法是动态规划完全背包,不过这会失去很多种组合方法,导致不能合成特定数。所以后来还是想到遍历,遍历的话只能是用DFS了。但是由于数据量最大是65,所以DFS时剪枝必须要到位。然后开始分析,1 首先枚举该特定数的大小时从给定数组的最大数开始遍历,到给定数组的和的一半,因为超过一半后,就只能是数组的和不能被特定数整除,无法合成该特定数,而小于数组的最大值显然是不行的,因为这样比这个数更大的数就无法利用了。2 其次每次枚举特定数时,需要判断一下能不能整除给定数组之和,如果不能就枚举下一个。3 DFS每次选取一个数时,不要直接从头开始遍历,而是用一个数组储存已经用过的数,然后再从未利用的数中查找。

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <map>
#include <climits>
using namespace std;
inline int read()//快速读入
{
    int x=0;
    bool f=1;
    char c=getchar();
    for(; !isdigit(c); c=getchar()) if(c=='-') f=0;
    for(; isdigit(c); c=getchar()) x=(x<<3)+(x<<1)+c-'0';
    if(f) return x;
    return 0-x;
}
int n,c[70],sum,then[70],x,m;//c[70]用来储存输入的数组,//then[70]记录与在给定数组中同下标处的数不同的数的下标
bool did[70],ans;//did[70]记录在该次遍历中已经用过的数,ans是判断是否已经找到答案
bool cmp(int a,int b)//从大到小排序
{
    return a>b;
}
void dfs(int which,int done,int pre)
{
    if(done==x)
    {
        if(which==m-2)
        {
            ans=true;
            return ;
        }
        for(int j=1; j<=n; j++)//选择一个没有用过的数开始遍历组成特定数
            if(!did[j])
            {
                did[j]=true;
                dfs(which+1,c[j],j);
                did[j]=false;
                return;//如果不能组成一个新数,说明这种组合不对,直接返回
            }
    }
    else
    {
        int will=x-done;
        int l=pre+1,r=n;
        while(l<r)
        {
            int mid=(r+l)/2;
            if(c[mid]>will)
                l=mid+1;
            else
                r=mid;
        }
        if(c[l]>will)
            return;
        for(int j=l; j<=n; )
        {
            if(!did[j])
            {
                did[j]=true;
                dfs(which,done+c[j],j);//pre=j,缩小范围;唉,对对对,之前一直写的是pre,慢了很多。
                did[j]=false;
                if(ans||will==c[j])
                    return ;
                j=then[j];
            }
            else
                j++;
        }
    }
}
int main()
{
    n=read();//输入个数
    for(int j=1; j<=n; j++)//输入数组
    {
        c[j]=read();
        sum+=c[j];
    }
    sort(c+1,c+1+n,cmp);//把给定数组从大到小排序
    for(int j=n; j>=1; j--)//记录在输入数组中与该数不同的下一个数的位置
    {
        if(c[j]==c[j+1])
            then[j]=then[j+1];
        else
            then[j]=j+1;
    }
    for(x=c[1]; x<=sum/2; x++)
    {
        if(sum%x==0)//判断能否利用全部数值完全组成特定数
        {
            m=sum/x;
            did[1]=true;//先把c[1]算上,就快了很多。。。。
            dfs(0,c[1],1);//遍历各个组合
            did[1]=false;
            if(ans)
            {
                printf("%d\n",x);
                return 0;
            }
        }
    }
    printf("%d\n",sum);//一定能组成的最大特定数
}

P1006 [NOIP2008 提高组] 传纸条
同时有两条路径的线性dp。

#include <iostream>
using namespace std;
int dp[60][60][60][60];
int main()
{
    int m,n;
    cin>>m>>n;
    int s[60][60]= {0};
    for(int j=1;j<=m;j++)
        for(int i=1;i<=n;i++)
            cin>>s[j][i];
    for(int j=1; j<=m; j++)
        for(int i=1; i<=n; i++)
            for(int k=1; k<=m; k++)
                for(int t=1; t<=n; t++)
                {
                    dp[j][i][k][t]=max(dp[j-1][i][k-1][t],max(dp[j][i-1][k-1][t],max(dp[j-1][i][k][t-1],dp[j][i-1][k][t-1])))+s[j][i]+s[k][t];
                    if(j==k&&i==t)
                        dp[j][i][k][t]-=s[j][i];
                }
    cout<<dp[m][n][m][n]<<endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值