0x5A~0x5B

0x5A~0x5B

0x5A 斜率优化

之前已经写过一些不再写一遍了。

Here

0x5B 四边形不等式

a.[√]诗人小G

题目传送

sol:

首先直接设出状态:\(f[i]\)表示前i首诗排版后的最小答案。

那么转移为:

\[f[i]=min_{0≤j<i}\{f[j]+|sum[i]-sum[j]+i-j-1-L|^P\} \]

由于存在高次,不符合单调队列优化和斜率优化,考虑四边形不等式优化。

\(val(j,i)=|sum[i]-sum[j]+i-j-1-L|^P\)

因为存在\(j<i\),那么只需证明\(val(j,i+1)+val(j+1,i)≥val(j,i)+val(j+1,i+1)\)

也即证明\(val(j+1,i)-val(j+1,i+1)≥val(j,i)-val(j,i+1)\)

\(a=(sum[i]+i)-(sum[j]+j)-(L+1),b=(sum[i]+i)-(sum[j+!]+j+1)-(L+1)\)

则只需证明:\(|a|^P-|a+(s[i+1]+1)|^P≥|b|^P-|b+(s[i+1]+1)|^P\)

由于\(a<b\),所以等价于只要证明函数\(y=|x|^P-|x+c|^P\ (c为常数)\)单调递减,分类讨论求导可证。

所以就可以套决策单调性的板子了。

code:

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB long double
using namespace std;

IL int gi() {
   RG int x=0,w=0; char ch=getchar();
   while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
   while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
   return w?-x:x;
}

const int N=1e5+3;

char ch[N][33];
int n,h,t,L,P,a[N],s[N];
DB f[N];

struct rec{int x,l,r;}q[N];

IL DB qpow(int x,int p) {
	RG DB a=x,ans=1.0;
	for(;p;p>>=1,a=a*a)
		if(p&1) ans=ans*a;
	return ans;
}

IL DB calc(int i,int j) {return f[j]+qpow(abs(s[i]-s[j]+i-j-1-L),P);}

IL int search(int i) {
	RG int l=q[t].l,r=q[t].r,mid;
	while(l<r) {
		mid=l+r>>1;
		if(calc(mid,i)<=calc(mid,q[t].x)) r=mid;
		else l=mid+1;
	}
	return r;
}

IL void print(int i) {
	if(!i) return;
	print(a[i]);
	for(RG int j=a[i]+1;j<=i;++j) {
		printf("%s",ch[j]);
		if(j!=i) putchar(' ');
	}
	putchar('\n');
}

int main()
{
   	RG int i,T=gi();
	while(T--) {
		n=gi(),L=gi(),P=gi();
		for(i=1;i<=n;++i)
			scanf("%s",ch[i]),s[i]=s[i-1]+strlen(ch[i]);
		h=1,t=0,q[++t]=(rec){0,1,n};
		for(i=1;i<=n;++i) {
			if(h<=t) {
				if(q[h].r==i-1) ++h;
				else q[h].l=i;
			}
			f[i]=calc(i,q[h].x),a[i]=q[h].x;
			RG int pos=n+1;
			while(h<=t) {
				if(calc(q[t].l,i)<=calc(q[t].l,q[t].x)) pos=q[t--].l;
				else {
					if(calc(q[t].r,i)<=calc(q[t].r,q[t].x)) 
						pos=search(i),q[t].r=pos-1;
					break;
				}
			}
			if(pos<=n) q[++t]=(rec){i,pos,n};
			// 决策i是pos~n的最优决策
		}
		if(f[n]>1e18) puts("Too hard to arrange");
		else printf("%lld\n",(LL)f[n]),print(n);
		puts("--------------------");
	}
    return 0;
}

b.[√] 合并石子

题目传送

sol:

\(O(n^3)\)的很熟悉了,直接写:

\[f[i,j]=\min_{i≤k<j}{}\{f[i,k]+f[k+1,j]\}+\sum_{k=i}^jA[k] \]

打表发现其具有决策单调性(不会证明),则可以使复杂度降为\(O(n^2)\)

值得一提的是:

3方DP中i是倒序枚举的,j是正序枚举的,则利用到的是:

\(p[i,j-1]≤p[i,j]≤p[i+1,j]\ (p[x,y]代表状态(x,y)的最优决策)\)

而对于其他可以用决策单调性优化的转移,若原始转移中i是正序枚举,j是倒序枚举,则有:

\(p[i-1,j]≤p[i,j]≤p[i,j+1]\ (p[x,y]代表状态(x,y)的最优决策)\)

应该只要符合决策单调性,就能套上这两种情况中的一种。

另外Luogu上的这题还有最大值要求,但最大值不满足决策单调性,但是它却必然是如下转移得到最优:

\(g[i,j]=max\{g[i,j-1],a[i+1,j]\}+\sum_{k=i}^jA[k]\) 原理应是贪心。

code:

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB double
using namespace std;

IL int gi() {
   RG int x=0,w=0; char ch=getchar();
   while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
   while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
   return w?-x:x;
}

const int N=903;

int n,ans1,ans2,a[N],s[N],f[N][N],g[N][N],pf[N][N],pg[N][N];

int main()
{
   	RG int i,j,k;
	memset(f,0x3f,sizeof(f));
	memset(g,0xcf,sizeof(g));
	for(i=1,n=gi();i<=n;++i) a[i]=a[i+n]=gi();
	for(i=1;i<=n<<1;++i)
		f[i][i]=g[i][i]=0,pf[i][i]=pg[i][i]=i,s[i]=s[i-1]+a[i];
	for(i=n<<1;i>=1;--i)
		for(j=i+1;j<=n<<1&&j-i+1<=n;++j) {
			for(k=pf[i][j-1];k<=pf[i+1][j];++k) 
				if(f[i][k]+f[k+1][j]+s[j]-s[i-1]<f[i][j])
					f[i][j]=f[i][k]+f[k+1][j]+s[j]-s[i-1],pf[i][j]=k;
			g[i][j]=max(g[i][j-1],g[i+1][j])+s[j]-s[i-1];
			//最大值没有决策单调性,但是为什么可以是上面那样?	
		}
	ans1=0x3f3f3f3f,ans2=0;
	for(i=1;i<=n;++i)
		ans1=min(ans1,f[i][i+n-1]),ans2=max(ans2,g[i][i+n-1]);
	printf("%d\n%d\n",ans1,ans2);
    return 0;
}

c.[√] 邮局

题目传送

sol:

此题不难想到设\(f[i,j]\)表示前面j个村庄建了i个邮局的最小距离和。

转移为:

\[f[i,j]=min_{0≤k<j}\{f[i-1,k]+cost(k+1,j)\} \]

关于cost的计算显然取中间值最优。

还是打表发现,f具有决策单调性。

那么注意到此处i正序枚举,j可正可倒,但为了套上面讲到的模型,让j倒序枚举,然后就有:

\(p[i-1,j]≤p[i,j]≤p[i,j+1]\ (p[x,y]代表状态(x,y)的最优决策)\)

但是由于每次枚举到下一个i时,\(p[i,\_]\)全为0,所以需要先把\(p[i,m+1]=m\),然后再转移。

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB double
using namespace std;

IL int gi() {
   RG int x=0,w=0; char ch=getchar();
   while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
   while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
   return w?-x:x;
}

const int N=303;
const int M=3003;

int n,m,a[M],f[N][M],p[N][M],pre[M][M],suf[M][M];

IL int dist(int l,int r,int mid) {return pre[mid][r]+suf[mid][l];}

IL int calc(int l,int r) {
    if((r-l+1)&1) return dist(l,r,l+r>>1);
    else return min(dist(l,r,l+r-1>>1),dist(l,r,l+r+1>>1));
}

int main()
{
    RG int i,j,k,tmp;
    m=gi(),n=gi();
    for(i=1;i<=m;++i) a[i]=gi();
    sort(a+1,a+m+1);
    for(i=1;i<=m;++i)
        for(j=i+1;j<=m;++j)
            pre[i][j]=pre[i][j-1]+a[j]-a[i];
    for(i=m;i>=1;--i)
        for(j=i-1;j>=1;--j)
            suf[i][j]=suf[i][j+1]+a[i]-a[j];
    memset(f,0x3f,sizeof(f));
    f[0][0]=0;
    for(i=1;i<=n;++i) {
		p[i][m+1]=m;
        for(j=m;j>=1;--j)
            for(k=p[i-1][j];k<=p[i][j+1];++k) {
                tmp=f[i-1][k]+calc(k+1,j);
                if(tmp<f[i][j]) f[i][j]=tmp,p[i][j]=k;
            }
	}
    printf("%d\n",f[n][m]);
	return 0;
}
posted @ 2019-06-11 21:51  薄荷凉了夏  阅读(689)  评论(0)    收藏  举报