关于标题
(S)Serious(E)Exercise(O)Of(N)Noip
怒怼Dp系列
T1
题目背景:
解题报告:
…一个比较简单且暴力的Dp题。。
先求出最多需要的工人数量减少后面枚举的次数。。
然后根据时间Dp就行了。。。
状态转移方程:
f[i][j] = min(f[i][j],f[i-1][k]+jud(k,j));
//i:天数;
//j:当天所用员工数;
//k:昨天所用员工数
int jud(int pre,int now)
{
if(pre>now)return (pre-now)*fire+now*fiee;
else return (now-pre)*hire+now*fiee;
}
my.cpp
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
int w=1,x=0;char ch;
while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*w;
}
int n,hire,fiee,fire,maxx;
int c[20],f[20][1000];
int jud(int pre,int now)
{
if(pre>now)return (pre-now)*fire+now*fiee;
else return (now-pre)*hire+now*fiee;
}
int main()
{
while((n=read())&&n!=0)
{
memset(f,63,sizeof(f));maxx=0;
hire=read();
fiee=read();
fire=read();
for(int i=1;i<=n;i++)cin>>c[i],maxx=max(maxx,c[i]);
f[0][0] = 0;
for(int i=1;i<=n;i++)
for(int j=c[i];j<=maxx;j++)
for(int k=0;k<=maxx;k++)
f[i][j] = min(f[i][j],f[i-1][k]+jud(k,j));
int ans=99999999;
for(int i=0;i<=maxx;i++)ans=min(ans,f[n][i]);
cout << ans << endl;
}
}
T2
题目背景:
解题报告:
注意这里老鼠只能到达在他所在位置的水平方向或竖直方向的洞穴前。
状态转移方程:
f[x][y] = dfs(xi,yi) + c[x][y];
//x,y:横,纵坐标。
//c[x][y]洞穴中奶酪的数目。
//边界:(xi<=n&&xi>=1&&yi<=n&&yi>=1)(c[xi][yi]>c[x][y])
my.cpp
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
int w=1,x=0;char ch;
while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*w;
}
int n,k,ans=0;
int c[105][105];
int f[105][105];
int dir[4]={0,0,1,-1};
int dfs(int x,int y)
{
int maxx=0;
if(!f[x][y])
{
for(int t=1;t<=k;t++)
{
for(int i=0;i<4;i++)
{
int xi = x+dir[i]*t;
int yi = y+dir[3-i]*t;
if(xi<=n&&xi>=1&&yi<=n&&yi>=1)
if(c[xi][yi]>c[x][y])
maxx = max(maxx,dfs(xi,yi));
}
}
f[x][y] = maxx+c[x][y];
}
return f[x][y];
}
int main()
{
while((n=read(),k=read())&&(n!=-1&&k!=-1))
{
memset(f,0,sizeof(f));
memset(c,0,sizeof(c));
ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
c[i][j] = read();
ans = max(ans,dfs(1,1));
cout << ans << endl;
}
}
T3
题目背景:
解题报告:
网上说是一道最长不下降序列的模板题。。。
然而我做了1个多小时orz
状态转移方程(经典):
pre[i]=((f[i]>=f[j]+1)?(pre[i]):(j));
//记录比它大的数,放在转移方程之前好比较。
f[i]=((f[i]>=f[j]+1)?(f[i]):(f[j]+1));
//i:前一个字符串正在比较的字符的下标
//j:后一个字符串正在比较的字符的下标
if(f[i]>ans1)ans1 = f[i],tot=i;
//记录答案的下标
tot=pre[tot];
//用链表(?)输出答案
my.cpp
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
int w=1,x=0;char ch;
while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*w;
}
struct node{int pos,wgt,spd;}mce[100050];
bool comp1(const node &a,const node &b)
{return a.wgt>=b.wgt;}
int cnt=1,tot=0;
int f[100050],pre[100050],ans[100050];
int main()
{
//freopen("hh.in","r",stdin);
while((scanf("%d%d",&mce[cnt].wgt,&mce[cnt].spd))!=EOF)
{mce[cnt].pos = cnt;f[cnt++]=1;}
cnt-=1;
sort(mce+1,mce+cnt+1,comp1);
int ans1=-1,st=0;
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<i;j++)
if(mce[i].spd>mce[j].spd&&mce[i].wgt<mce[j].wgt)
{
pre[i] = ((f[i]>=f[j]+1)?(pre[i]):(j));
f[i] = ((f[i]>=f[j]+1)?(f[i]):(f[j]+1));
}
if(f[i]>ans1)ans1 = f[i],tot=i;
}
cout << ans1 << endl;
cnt=0;
while(tot){cout<<mce[tot].pos<<endl;tot=pre[tot];}
}
T4
题目背景:
解题报告:
状态转移方程
for(int i=1;i<=m;i++)
{
int cas=0;
for(int j=1;j<=i;j++)cas += a[j];
}
//预处理
cas = max(premax[j-1],cas)+a[j];
premax[j-1] = premax[n];
premax[n] = max(premax[n],cas);
//i:区间起点
//j:以该下标为终点的最大区间和
附带标准最大区间和O(1)的转移方程与友情链接:
//基于数组的:maxn[i]=max{0,maxn[i-1]}+a[i]
for(int i=1;i<=n;i++)
{
last = max(0,last)+a[i];
ans = max(ans,last);
}
//基于前缀和的:ans=max{sum[i]-min{sum[j]} | 0<=j<i<=n }
for(int i=1;i<=n;i++)
{
ans = max(ans,sum[i]-minn);
minn = min(minn,sum[i]);
}
my.cpp
//给定由 n个整数(可能为负整数)组成的序列a1,a2,a3,……,an,以及一个正整数 m,要求确定序列 a1,a2,a3,……,an的 m个不相交子段,
//使这m个子段的总和达到最大,求出最大和。
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
int w=1,x=0;char ch;
while(ch< '0'||ch >'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*w;
}
int a[1000050];
int premax[1000050];
int m,n;
int dp(int n,int m)
{
memset(premax,0,sizeof(premax));
for(int i=1;i<=m;i++)
{
int cas=0;
for(int j=1;j<=i;j++)cas += a[j];
premax[n] = cas;
for(int j=i+1;j<=n;j++)
{
cas = max(premax[j-1],cas)+a[j];
premax[j-1] = premax[n];
premax[n] = max(premax[n],cas);
}
}
return premax[n];
}
int main()
{
while(cin>>m>>n)
{
for(int i=1;i<=n;i++)cin>>a[i],premax[i]=0;
cout << dp(n,m) << endl;
}
}
总结
果然多刷题效果就好很多。
接下来每天除了应付测试再做两道Dp,顺便在背一个模板。
(妈的我要在考试前背完21个模板和对应的样例还要在复赛考试前打到计算机上。。。)