1. 程式人生 > 實用技巧 >Gym 101196F Removal Game

Gym 101196F Removal Game

https://codeforces.com/gym/101196/attachments

環形/區間DP

先將區間複製一遍

\(f[i][j]\)表示區間\([i,j]\)刪去\([i+1,j-1]\)的數的最小代價

之所以這樣定義是因為這樣比較好轉移

\[f[i][j]=\min_{i<k<j} f[i][k]+f[k][j]+\gcd(a[i],a[j]) \]

最後列舉兩個端點,再次計算\(\gcd\)即可

\[ans=\min_{1\le i<j \le n} f[i][j]+f[j][i+n]+\gcd(a[i],a[j]) \]

\(C++ Code:\)

#include<cstdio>
#include<iostream>
#define N 205
#define INF 1000000007
using namespace std;
int n,a[N],f[N][N],g[N][N];
int gcd(int x,int y)
{
	if (!y)
		return x;
	return gcd(y,x%y);
}
int main()
{
	for (;;)
	{
		scanf("%d",&n);
		if (!n)
			return 0;
		for (int i=1;i<=n;i++)
			scanf("%d",&a[i]),a[i+n]=a[i];
		int m=n << 1;
		for (int i=1;i<=m;i++)
			for (int j=1;j<=m;j++)
				g[i][j]=gcd(a[i],a[j]);
		for (int i=0;i<=m;i++)
			for (int j=0;j<=m;j++)
				f[i][j]=INF;
		for (int i=1;i<=m;i++)
			f[i][i+1]=0;
		for (int i=3;i<=n;i++)
		{
			for (int j=1;j<=m-i+1;j++)
			{
				int r=j+i-1;
				for (int k=j+1;k<r;k++)
					f[j][r]=min(f[j][r],f[j][k]+f[k][r]+g[j][r]);
			}
		}
		int ans=INF;
		for (int i=1;i<=n;i++)
			for (int j=i+1;j<=n;j++)
				ans=min(ans,f[i][j]+f[j][i+n]+g[i][j]);
		printf("%d\n",ans);
	}
}