NOI2009 诗人小G
题目链接:戳我
30pts做法:
我们设\(dp[i]\)表示前i个句子最小的代价,然后再记录一个当前的最小值是从哪里转移过来的。
注意输出的行末不带空格,以及要用long double(double和long long都会炸)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define MAXN 100010
#define mp make_pair
#define ll long long
#define double long double
#define INF 1e18
using namespace std;
int T,n,l,p;
char s[MAXN][60];
double sum[MAXN];
inline double calc(double x)
{
double cur_ans=1,xx=1.0*x;
for(int i=1;i<=p;i++) cur_ans*=xx;
return cur_ans;
}
namespace subtask1
{
int last[MAXN];
double dp[MAXN];
vector<pair<int,int> >vec;
inline void solve()
{
vec.clear();
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
for(int i=0;i<=n;i++) dp[i]=INF*8;
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<i;j++)
{
double cur_ans=dp[j]+calc(fabs(1.0*l-(1.0*i-j-1+sum[i]-sum[j])));
if(cur_ans<dp[i])
{
last[i]=j+1;
dp[i]=cur_ans;
}
}
}
if(dp[n]>INF)
{
printf("Too hard to arrange\n");
return;
}
printf("%.0Lf\n",dp[n]);
int now=n;
while(now>=1)
{
vec.push_back(mp(last[now],now));
now=last[now]-1;
}
for(int i=vec.size()-1;i>=0;i--)
{
for(int j=vec[i].first;j<vec[i].second;j++)
printf("%s ",s[j]+1);
printf("%s",s[vec[i].second]+1);
puts("");
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&l,&p);
for(int i=1;i<=n;i++) scanf("%s",s[i]+1),sum[i]=1.0*strlen(s[i]+1);
subtask1::solve();
printf("--------------------\n");
}
return 0;
}
100分做法需要决策单调性
我们考虑记录s数组表示前缀和,\(f[i]\)表示前i个的最小代价。
\(f[i]=min{f[j]+|s[i]-s[j]-1-L|}\)
其中\(j\in [0,i-1]\)
显然对于两个点j,k,且\(j<k\)。如果j能够转移到i,k能转移到i+1,那么j一定不能转移到其它大于i的点。
那么这就存在一个决策单调的性质。
我们可以维护一下么个点的作用范围。用一个单调栈加三元组记录就可以了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#define MAXN 100010
#define double long double
using namespace std;
int n,L,P,T,head,tail,top;
int g[MAXN],S[MAXN];
char ch[MAXN][35];
long long s[MAXN];
double f[MAXN];
struct Node{int j,l,r;}q[MAXN];
inline double fpow(double x,int y)
{
double cur_ans=1;
while(y)
{
if(y&1) cur_ans=cur_ans*x;
x=x*x;
y>>=1;
}
return cur_ans;
}
inline double calc(int i,int j)
{
return f[j]+fpow(fabs(1.0*s[i]-s[j]-1-L),P);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
freopen("ce.out","w",stdout);
#endif
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&L,&P);
for(int i=1;i<=n;i++)
{
scanf("%s",ch[i]+1);
s[i]=1ll*strlen(ch[i]+1);
}
for(int i=1;i<=n;i++) s[i]+=s[i-1]+1;
memset(q,0,sizeof(q));
q[head=tail=1]=(Node){0,1,n};
for(int i=1;i<=n;i++)
{
while(head<tail&&q[head].r<i) ++head;
int j=q[head].j;
q[head].l++;
f[i]=calc(i,j);
g[i]=j;
while(head<tail&&calc(q[tail].l,q[tail].j)>=calc(q[tail].l,i)) tail--;
int l=q[tail].l,r=q[tail].r,cur_ans=q[tail].r+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(calc(mid,i)<=calc(mid,q[tail].j)) cur_ans=mid,r=mid-1;
else l=mid+1;
}
if(cur_ans!=q[tail].l) q[tail].r=cur_ans-1;
else --tail;
if(cur_ans<=n) q[++tail]=(Node){i,cur_ans,n};
}
if(f[n]>1e18) printf("Too hard to arrange\n");
else
{
printf("%.0Lf\n",f[n]);
top=0;
for(int i=n;i;i=g[i])S[++top]=i;S[top+1]=0;
for(int i=top;i;--i)
for(int j=S[i+1]+1;j<=S[i];++j)
{
printf("%s",ch[j]+1);
if(j!=S[i]) printf(" ");
else printf("\n");
}
}
printf("--------------------\n");
}
return 0;
}