1 条题解
-
0
自动搬运
来自洛谷,原作者为

Treeloveswater
**搬运于
2025-08-24 22:02:52,当前版本为作者最后更新于2018-06-22 09:58:42,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
Emmmm和出题人交流了一下,发现自己的算法没问题
也成功拿到最快的44ms
我来说一下我的做法吧,这个做法的复杂度非常的优越
首先我们肯定是要 2^k去枚举每个部分的状态的
复杂度优化的关键在于Dp
我们发现如果固定扣的分数,那么行数自然是尽量大。在行数一样大的情况下自然是最后一行字越多越好。
我们发现可以在dp的基础上贪心。
我们设f[i][j]为 dp到第i个句子,现在扣了j分
f[i][j]是一个pair,保存着两个值 行数 最后一行的字数
这样Dp的复杂度是 n·cnt·S cnt是不会扣到0的部分的数量
2^k种状态的cnt加和是 k*2^(k-1)
所以最后的复杂度是 2^(k-1)kSn == 80 · 200 · 200
非常优越的复杂度,甚至可以把S,n提高到300都可以做
附上代码:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; pair<int,int> f[201][1005]; int n,m,L,K,S,C; int A[205]; int s[205][11]; bool vis[201][1005],ok[11]; void up(int i,int j,int a,int b){ if(!vis[i][j]){ vis[i][j]=1; f[i][j]=make_pair(a,b); } else{ if(f[i][j].first>a)return; if(f[i][j].first<a) f[i][j]=make_pair(a,b); else f[i][j].second=max(f[i][j].second,b); } } int main(){ scanf("%d%d%d%d%d%d",&n,&m,&L,&K,&S,&C); for(int i=1;i<=n;i++) scanf("%d",&A[i]); for(int i=1;i<n;i++) for(int j=0;j<K;j++) scanf("%d",&s[i][j]); int ans=0; for(int t=1;t<(1<<K);t++){ memset(vis,0,sizeof(vis)); for(int i=0;i<K;i++) ok[i]=(t>>i)&1; int cnt=0,a,b,c,d,e; for(int i=0;i<K;i++) cnt+=ok[i]; a=(A[1]+2)/L;b=(A[1]+2)%L; if(!b) b=L; else a++; f[1][0]=make_pair(a,b);vis[1][0]=1; for(int i=1;i<n;i++) for(int j=0;j<=cnt*S;j++) if(vis[i][j]){ a=f[i][j].first,b=f[i][j].second; e=0; c=a-1+(b+A[i+1])/L,d=(b+A[i+1])%L; if(!d) d=L; else c++; for(int k=0;k<K;k++) if(ok[k]&&s[i][k]<0) e-=s[i][k]; if(j+e<=cnt*S) up(i+1,j+e,c,d); e=0; c=a+(A[i+1]+2)/L,d=(A[i+1]+2)%L; if(!d) d=L; else c++; for(int k=0;k<K;k++) if(ok[k]&&s[i][k]>0) e+=s[i][k]; if(j+e<=cnt*S) up(i+1,j+e,c,d); } int total=cnt*S,zm; for(int j=0;j<=cnt*S;j++) if(vis[n][j]){ zm=total-j; if(f[n][j].first<m) zm-=(m-f[n][j].first)*C; ans=max(ans,zm); } } cout<<ans<<endl; }
- 1
信息
- ID
- 3447
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者