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;
}