解题思路
看到
N
<
=
15
N<=15
N<=15,想到状压DP,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示状态为
i
,
i
i,i
i,i为
0
/
1
0/1
0/1二进制数表示哪一位 没用过/用过 的状态,
j
j
j表示现在最右的农田为第j个农田,得:
枚举上一个状态
i
i
i,上一个状态最右的农田
j
j
j,当前状态最右的农田
k
k
k。这都这些可以推出当前状态含
k
k
k也含
j
j
j,
t
=
i
∣
(
1
<
<
(
k
−
1
)
)
t=i|(1<<(k-1))
t=i∣(1<<(k−1))
- 当 a [ j ] < a [ k ] : f [ t ] [ k ] = f [ i ] [ j ] a[j]<a[k]:f[t][k]=f[i][j] a[j]<a[k]:f[t][k]=f[i][j]
- 当 a [ j ] > = a [ k ] : f [ t ] [ k ] = f [ i ] [ j ] + ( a [ k ] − a [ j ] ) ∗ 2 a[j]>=a[k]:f[t][k]=f[i][j]+(a[k]-a[j])*2 a[j]>=a[k]:f[t][k]=f[i][j]+(a[k]−a[j])∗2
方案数:我们在DP的时候顺便记录方案数,更新 f f f 时也更新方案数 p p p,当 f 相同时p就进行累加, p [ t ] [ k ] + = p [ i ] [ j ] p[t][k]+=p[i][j] p[t][k]+=p[i][j],否则 p [ t ] [ k ] = p [ i ] [ j ] p[t][k]=p[i][j] p[t][k]=p[i][j]
代码
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cmath>
using namespace std;
int n;
long long a[20],f[1<<15][20],ans1,ans2,p[1<<15][20];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=0;i<n;i++)
{
f[1<<i][i+1]=2*a[i+1];
p[1<<i][i+1]=1;
}
for(int i=1;i<(1<<n);i++)
{
for(int j=1;j<=n;j++)
{
if((i>>(j-1))&1)
{
for(int k=1;k<=n;k++)
{
if((i>>(k-1))&1)continue;
int t=i|(1<<(k-1));
long long x;
if(a[j]>a[k])x=f[i][j];
else x=f[i][j]+(a[k]-a[j])*2;
if(x>f[t][k])
f[t][k]=x,p[t][k]=p[i][j];
else if(x==f[t][k])
p[t][k]+=p[i][j];
}
}
}
}
for(int i=1;i<=n;i++)
ans1=max(ans1,f[(1<<n)-1][i]);
for(int i=1;i<=n;i++)
if(f[(1<<n)-1][i]==ans1)
ans2+=p[(1<<n)-1][i];
printf("%lld %lld",ans1+2*n,ans2);//ans1加上长和宽
}
/*
14
99 99 99 99 99 99 99 99 99 99 99 99 99 1
*/