题目链接
http://www.lydsy.com/JudgeOnline/problem.php?id=1407
思路
由于此题里
n
的范围很小,因此可以直接从小到大暴力枚举洞穴个数
(step i −step j )x≡pos j −pos i (modm)
我们要求的就是这个同余式的最小解 x min
可以转换为下面的式子
(step i −step j )x+my=pos j −pos i
由于上面的 x,y 的系数并不是两两互质,因此需要先对所有的系数和常数除以 gcd 。可以通过扩展欧几里得找到一个 (x 0 ,y 0 ) ,显然
step i −step j gcd(step i −step j ,m) x 0 +mgcd(step i −step j ,m) y 0 =pos j −pos i gcd(step i −step j ,m)
显然一个可行解是 (gcd(step i −step j ,m)x 0 ,gcd(step i −step j ,m)y 0 )
x 的通解满足
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 80
using namespace std;
struct Node
{
int val; //键值
int weight; //权值
int frequence; //频率
}node[MAXN];
int n,K;
bool cmp(Node a,Node b)
{
return a.val<b.val;
}
int f[MAXN][MAXN][MAXN],stack[MAXN],top=0; //边界:f[i][i-1][w]=0,表示点i为根结点,下面无左儿子(i-1为根节点,下面无右儿子),显然f值为0
int sum[MAXN]; //频率的前缀和
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&node[i].val);
for(int i=1;i<=n;i++)
scanf("%d",&node[i].weight),stack[++top]=node[i].weight;
for(int i=1;i<=n;i++)
scanf("%d",&node[i].frequence);
sort(stack+1,stack+top+1);
for(int i=1;i<=n;i++)
node[i].weight=lower_bound(stack+1,stack+top+1,node[i].weight)-stack;
sort(node+1,node+n+1,cmp);
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+node[i].frequence;
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n+1;i++)
for(int w=0;w<=n;w++)
f[i][i-1][w]=0;
for(int w=n;w>=1;w--)
for(int i=n;i>=1;i--)
for(int j=i;j<=n;j++)
for(int k=i;k<=j;k++) //枚举[i,j]这段区间的点对应的子树,该子树以点k作为根节点
{
f[i][j][w]=min(f[i][j][w],f[i][k-1][w]+f[k+1][j][w]+K+sum[j]-sum[i-1]); //k本来权值小于K,将其权值变为k
if(node[k].weight>=w) f[i][j][w]=min(f[i][j][w],f[i][k-1][node[k].weight]+f[k+1][j][node[k].weight]+sum[j]-sum[i-1]); //k的权值本身就大于等于K,因此可以不变
}
int ans=0x3f3f3f3f;
for(int w=0;w<=n;w++)
ans=min(ans,f[1][n][w]);
printf("%d\n",ans);
return 0;
}