数字游戏
这是一个很好的区间dp题,个人感觉,容易想到,但是不容易写出来,边界处理很强,预处理出,前缀和,最小值需要初始化,破坏环成为两倍的大小的数组,细节很多。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int p=1e7+233;
const double pi=3.141592653589;
int dpma[105][105][12],dpmi[105][105][12];
int a[105];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i+n]=a[i];//破坏环
}
ms(dpmi,inf);
for(int i=2;i<=2*n;i++)a[i]+=a[i-1];//前缀和
for(int i=1;i<=2*n;i++)
{
for(int j=i;j<=2*n;j++)
{
dpma[i][j][1]=dpmi[i][j][1]=((a[j]-a[i-1])%10+10)%10;//预处理
}
}
for(int i=2;i<=m;i++)//枚举断点个数
{
for(int l=1;l<=2*n;l++)//左端点
{
for(int r=l+i-1;r<=2*n;r++)//右端点
{
for(int k=l+i-2;k<r;k++)//枚举断点位置
{
dpma[l][r][i]=max(dpma[l][k][i-1]*(((a[r]-a[k])%10+10)%10),dpma[l][r][i]);
dpmi[l][r][i]=min(dpmi[l][k][i-1]*(((a[r]-a[k])%10+10)%10),dpmi[l][r][i]);
}
}
}
}
int ma=0,mi=inf;
for(int i=1;i<=n;i++)
{
ma=max(ma,dpma[i][i+n-1][m]);
mi=min(mi,dpmi[i][i+n-1][m]);
}
printf("%d\n%d\n",mi,ma);
return 0;
}
P1052 过河
通过这个题我学到许多,这种路径压缩是真的强,放在我是想不到,我想的是离散,然而离散的话就不好控制特么之间的距离,路径压缩我感觉是离散的一种拓展,和离散十分的相似(个人感觉)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int a[105],flage[maxn],dp[maxn];//看好空间大小啊
int main()
{
int L;
int S,T,M;
scanf("%d%d%d%d",&L,&S,&T,&M);
if(S==T)//一定要特判啊
{
int x,cnt=0;
for(int i=1;i<=M;i++)
{
scanf("%d",&x);
cnt+=(x%S==0);
}
printf("%d\n",cnt);
return 0;
}
for(int i=1;i<=M;i++)
{
scanf("%d",&a[i]);
}
sort(a+1,a+1+M);
a[M+1]=L;
ms(dp,inf);
dp[0]=0;
a[0]=0;
int p=0;
for(int i=1;i<=M;i++)
{
int far=min(90,a[i]-a[i-1]);//如果想看证明过程的话请到洛谷去看,可以通过上面的链接进入,也可以自己去搜索,进去后第一个题解有详细证明过程。其实只要开到(T+1)*T-T-(T+1)也就是71就足够了
p+=far;
flage[p]=1;
}
p+=min(90,L-a[M]);
for(int i=0;i<=p+T-1;i++)
{
for(int j=S;j<=T;j++)
{
if(i-j>=0)
dp[i]=min(dp[i],dp[i-j]+flage[i]);
}
}
int ans=inf;
for(int i=p;i<=p+T-1;i++)//因为你可能是条过了桥,可能从L-1跳到L-1+(S到T内的一个数),也可能是l-2+(S到K内的一个数),所以答案在p到p+T-1内
{
ans=min(ans,dp[i]);
}
printf("%d\n",ans);
return 0;
}
P1095 守望者的逃离
本题主要是要想到把位移与休息和跑步分开分别求一次dp,这样就十分简单了
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =3e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int dp[maxn];
int main()
{
int m,s,t;
scanf("%d%d%d",&m,&s,&t);
for(int i=1;i<=t;i++)
{
if(m>=10)
dp[i]=dp[i-1]+60,m-=10;
else
m+=4,dp[i]=dp[i-1];
}
for(int i=1;i<=t;i++)
{
dp[i]=max(dp[i],dp[i-1]+17);
}
if(dp[t]<s)
{
printf("No\n%d\n",dp[t]);
}
else
{
printf("Yes\n");
for(int i=1;i<=t;i++)
{
if(dp[i]>=s)
{
printf("%d\n",i);
return 0;
}
}
}
return 0;
}
P1108 低价购买
没想到这么容易的一个题目我看题解都看了半天,我也是醉了,还是不喜欢独立思考啊,这毛病要改啊,
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =3e5 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int dp[maxn],n,a[maxn],f[maxn];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int ma=1;
for(int i=1;i<=n;i++)
{
dp[i]=1;
for(int j=1;j<i;j++)
{
if(a[i]<a[j])
{
dp[i]=max(dp[i],dp[j]+1);
ma=max(ma,dp[i]);
}
}
}
for(int i=1;i<=n;i++)
{
if(dp[i]==1)f[i]=1;
for(int j=1;j<i;j++)
{
if(dp[i]==dp[j]+1&&a[i]<a[j])//如果dp[j]+1==dp[i]并且a[i]
{
f[i]+=f[j];
}
else if(dp[i]==dp[j]&&a[i]==a[j])//如果两个都相等的话舍弃一个对结果没有影响
f[j]=0;
}
}
int sum=0;
for(int i=1;i<=n;i++)
{
if(dp[i]==ma)sum+=f[i];
}
printf("%d %d\n",ma,sum);
return 0;
}
P1099 树网的核
本题是树形dp之类的题目很容易,但是又不好想,首先树的直径肯定是要求的,然后在树的直径上尺取法找最大值中的最小值,但是要考虑的是,如果s大于树的直径的话,那么这样求出来的可能就是0了,但是如果他还有分支的话,那么绝对不可能是0,所有还要求其他的点到树的直径的距离和答案取个最大值就可了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e3 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int n,s,pa[maxn],max_len=0,dis[maxn],k=0,vis[maxn];
vector<pair<int,int> >ve[maxn];
void dfs(int u,int f)
{
pa[u]=f;
if(dis[k]<dis[u])k=u;
for(int i=0;i<ve[u].size();i++)
{
int to=ve[u][i].first;
if(to==f||vis[to])continue;
dis[to]=dis[u]+ve[u][i].second;
dfs(to,u);
}
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
ve[u].push_back({v,w});
ve[v].push_back({u,w});
}
dis[1]=0;
dfs(1,-1);
int st=k;
dis[k]=0;
dfs(k,-1);
int ed=k;
int ans=inf;
for(int i=ed,j=ed;i+1&&j+1;i=pa[i])
{
while(dis[j]-dis[i]>s)j=pa[j];
ans=min(ans,max(dis[i],dis[ed]-dis[j]));
}
for(int i=ed;i+1;i=pa[i])vis[i]=1;
for(int i=ed;i+1;i=pa[i])
{
k=i,dis[k]=0;
dfs(i,pa[i]);
}
for(int i=1;i<=n;i++)
{
ans=max(ans,dis[i]);
}
printf("%d\n",ans);
return 0;
}
P3399 丝绸之路
,本题我采用的是单调队列优化dp,但是我看题解没有写的这么麻烦,但是做出来了就是王道,其实三重循环加o2也可以过,不加o2直接超时,加了单调队列100ms,还是可以接受的,我看其他人的就50ms左右得去学习一下。
,不加单调队列的暴力写法
for(int i=1;i<=n;i++)
{
for(int j=i;j<=m;j++)
{
for(int k=i-1;k<=j-1;k++)
dp[i][j]=min(dp[i-1][k]+a[i]*b[j],dp[i][j]);
}
}
,加了单调队列的写法
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e3 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int a[maxn],dp[1005][1005],b[maxn];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d",&b[i]);
}
ms(dp,inf);
for(int j=0;j<=m;j++)
dp[0][j]=0;
deque<int>q;
for(int i=1;i<=n;i++)
{
q.push_back(i-1);
for(int j=i;j<=m;j++)
{
dp[i][j]=min(dp[i-1][q.front()]+a[i]*b[j],dp[i][j]);
while(dp[i-1][q.front()]>=dp[i-1][j]&&!q.empty())
q.pop_back();
q.push_back(j);
//bug;
}
while(!q.empty())q.pop_back();
}
int ans=inf;
for(int j=n;j<=m;j++)
ans=min(ans,dp[n][j]);
printf("%d\n",ans);
return 0;
}
.哇哦,我取经回来了原来是我定义的dp[i][j]是到达第i个城市用了j天,大佬的是第i天到达第j个城市代码直接给上
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e3 + 5;
const int maxm = 1.5e5+50;
const double eps = 1e-7;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int a[maxn],b[maxn],dp[1005][1004];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
for(int j=1;j<=m;j++)
{
scanf("%d",&b[j]);
}
ms(dp,inf);
dp[0][0]=0;
for(int i=1;i<=m;i++)
{
for(int j=0;j<=min(n,i);j++)
{
if(j==i)dp[i][j]=dp[i-1][j-1]+a[j]*b[i];
else
dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+a[j]*b[i]);//dp[i-1][j]代表第i天休息但已经到达j, 与第i-1天走到j花费的最小值
}
}
int ans=inf;
for(int i=n;i<=m;i++)
ans=min(ans,dp[i][n]);
printf("%d\n",ans);
return 0;
}
POJ - 1651
,最近唯一自己写出来的dp题目很容易,而且还是自己写过一个类似的题目才写出来的,,要加油了啊,不要拖后腿啊,由于是自己写出来的就不写题解了,直接上代码吧
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e6+10;
const int maxm = 1.5e5+50;
const double eps = 1e-18;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int a[1005],dp[1005][1005];
int main()
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
ms(dp,inf);
for(int i=1;i<=n;i++)
{
dp[i][i+2]=a[i]*a[i+1]*a[i+2];
dp[i][i]=dp[i][i+1]=0;
}
for(int len=3;len<=n;len++)
{
for(int l=1;l+len-1<=n;l++)
{
int r=len+l-1;
for(int k=l+1;k<r;k++)
{
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k][r]+a[l]*a[k]*a[r]);
}
}
}
printf("%d\n",dp[1][n]);
return 0;
}
HDU - 2476
,还是区间dp问题,不过这一个思路倒是没错,但是写的顺序撮了第一个dp方程一定要写出来很经典,第二个就要想一想了
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =1e6+10;
const int maxm = 1.5e5+50;
const double eps = 1e-18;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
char a[105],b[105];
int dp[105][105],f[105];
int main()
{
while(~scanf("%s%s",a+1,b+1))
{
int n=strlen(a+1);
ms(dp,0);
for(int i=1; i<=n; i++)dp[i][i]=1;
for(int len=2; len<=n; len++)
{
for(int l=1; l+len-1<=n; l++)
{
int r=l+len-1;
if(b[l]==b[r])
dp[l][r]=min(dp[l][r-1],dp[l+1][r]);
else
dp[l][r]=min(dp[l][r-1]+1,dp[l+1][r]+1);
for(int k=l; k<=r; k++)
{
dp[l][r]=min(dp[l][k]+dp[k+1][r],dp[l][r]);
}
}
}
for(int i=1;i<=n;i++)
f[i]=dp[1][i];
for(int i=1;i<=n;i++)
{
if(a[i]==b[i])
{
f[i]=f[i-1];
}
else
{
for(int k=1;k<i;k++)
{
f[i]=min(f[i],f[k]+dp[k+1][i]);
}
}
}
printf("%d\n",f[n]);
}
return 0;
}
You Are the One HDU - 4283
本题还是区间dp的题我还是看了好久的题解,害写出来以后发现就那样,枚举区间长度,枚举左端点,找到有端点,枚举断点没了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define ms(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x & -x
#define fi first
#define ull unsigned long long
#define se second
#define endl "\n"
#define bug cout<<"----acac----"<
#define IOS ios::sync_with_stdio(false), cin.tie(0),cout.tie(0)
using namespace std;
const int maxn =2e2+10;
const int maxm = 1.5e5+50;
const double eps = 1e-18;
const double inf = 0x3f3f3f3f;
const double lnf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const double pi=3.141592653589;
int a[maxn],dp[maxn][maxn],sum[maxn];
int main()
{
int T;
scanf("%d",&T);
int cas=1;
while(T--)
{
ms(dp,inf);
ms(sum,0);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][i]=0;
sum[i]=sum[i-1]+a[i];
}
for(int len=2;len<=n;len++)
{
for(int l=1;l+len-1<=n;l++)
{
int r=len+l-1;
dp[l][r]=min(dp[l][r],dp[l+1][r]+(r-l)*a[l]);
for(int k=l;k<=r;k++)
{
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]+(k-l+1)*(sum[r]-sum[k]));//这里因为要把dp[k+1][r]和之前的那个区间合并,所以之前的那个区间先是全部上完了,然后这个区间再上,所以要加上,上一个区间的区间长度乘以他们的屌丝值,
//dp[i][j](只考虑i到j这个区间内值不考虑其他的,也就是说现在只有i到j这么多人在参见这个活动)代表i到j内屌丝值乘以上场顺序的最小值
}
}
}
printf("Case #%d: %d\n",cas++,dp[1][n]);
}
return 0;
}