https://cn.vjudge.net/contest/233829#problem/A
题意:
把数组分成m-1段,保证每一段的数量都是偶数个,每半段的数量不会超过d,求最大的半段质量的最小值
分析:
有最大值的最小值就是二分,这个题我感觉比较难地方就是满足数量m,就一定满足m+2*n(n>=0),这个做这个题的重点,我想了好久
代码:
#include<bits/stdc++.h>
#define ll unsigned long long
#define inf 1e9+7
using namespace std;
int n,m,d,dp[40005][2],num;
ll sum[40005],a[40005];
bool ok(ll maxn)
{
int i,j;
dp[0][0]=0;
dp[0][1]=inf;
// cout<<"ssss "<<maxn<<endl;
for(i=1;i<=num;i++)
{
dp[i][0]=dp[i][1]=inf;
for(j=i-1;j>=0&&i-j<=d;j--)
{
if(sum[i*2]-sum[i+j]>maxn)break;
if(sum[i+j]-sum[2*j]<=maxn)
{
dp[i][0]=min(dp[i][0],dp[j][1]+1);
dp[i][1]=min(dp[i][1],dp[j][0]+1);
}
}
//cout<<dp[i][0]<<" ";
}
return dp[num][m%2]<=m;
}
int main()
{
int i,j,t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&d);
sum[0]=0;
for(i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
if(n&1||(m-1)*2*d<n||n<2*(m-1)||m==1)
{
printf("BAD\n");
continue;
}
m--;
num=n>>1;
ll l,r,mid;
l=1;
r=sum[n];
while(l<r)
{
mid=(l+r)>>1;
if(ok(mid))r=mid;
else
l=mid+1;
}
printf("%d\n",l);
}
}
https://cn.vjudge.net/contest/233829#problem/I
题意:
就是走所有的点,怎么花费最小
分析:
就是区间dp,都是一些细节问题
但是我自己做就是一直wrong,我也不知道哪里出了错,然后看了下题解,是用深搜实现的区间dp,确实省去了许多细节问题
代码:
#include<bits/stdc++.h>
#define ll unsigned long long
const double inf = 1e30;
using namespace std;
int key;
struct node
{
int x;
double c,val;
bool operator<(const node &aa)const
{
return x<aa.x;
}
}v[1005];
double pre[1005],dp[1005][1005][2];
int vis[1005][1005][2];
int n,vv,pos;
double cost(int l,int r,int s,int t)
{
double result = 0.0;
double sumd = pre[n] - (pre[r] - pre[l] + v[l].val);
int dist = abs(v[s].x - v[t].x);
double tm = (double)dist / (double)vv;
result = sumd*tm;
return result;
}
double dfs(int l,int r,int k)
{
if(l==0&&r==n)return 0;
double &ans=dp[l][r][k];
if(vis[l][r][k]==key)return ans;
ans=inf;
vis[l][r][k]=key;
int q;
if(k==0)q=l;
else
q=r;
if(l>0)
ans=min(ans,dfs(l-1,r,0)+cost(l,r,q,l-1));
if(r<n)
ans=min(ans,dfs(l,r+1,1)+cost(l,r,q,r+1));
return ans;
}
int main()
{
int p;
key=0;
memset(vis,0,sizeof(vis));
while(~scanf("%d%d%d",&n,&vv,&pos))
{
key++;
if(n==0&&vv==0&&pos==0)break;
int i,j;
double sum;
sum=0;
for(i=1;i<=n;i++)
{
scanf("%d%lf%lf",&v[i].x,&v[i].c,&v[i].val);
sum+=v[i].c;
}
v[0].x=pos;
v[0].c=0;
v[0].val=0;
n;
sort(v,v+n+1);
for(i=0;i<n+1;i++)
{
pre[i]=v[i].val;
if(v[i].x==pos)p=i;
if(i)pre[i]+=pre[i-1];
}
printf("%0.f\n",floor(dfs(p,p,1)+sum));
}
}
https://cn.vjudge.net/contest/233829#problem/K
题意:跟上一个题类似,都是区间dp的题
就是删除相同的颜色,获得一个价值,问怎么删除价值最大
分析:
是一个区间dp的问题,就是在处理颜色的时候重点分析的最后一个方格,然后不断地深搜下去
感觉这个题比较难
代码:
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int MAXN = 210;
int len[MAXN],dp[MAXN][MAXN][MAXN],n,color[MAXN],cnt;
int dfs(int x,int y,int k){
if (dp[x][y][k])
return dp[x][y][k];
if (x == y)
return (len[x]+k)*(len[x]+k);
dp[x][y][k] = dfs(x,y-1,0)+(len[y]+k)*(len[y]+k);
for (int i = x; i < y; i++){
if (color[y] == color[i])
dp[x][y][k] = max(dp[x][y][k],dfs(x,i,len[y]+k)+dfs(i+1,y-1,0));
}
return dp[x][y][k];
}
int main(){
int T,ans;
scanf("%d",&T);
for (int t = 1; t <= T; t++){
printf("Case %d: ",t);
scanf("%d",&n);
cnt = 0;
memset(len,0,sizeof(len));
memset(color,0,sizeof(color));
int temp;
for (int i = 0; i < n; i++){
scanf("%d",&temp);
if (color[cnt] == temp)
len[cnt]++;
else {
cnt++;
len[cnt]++;
color[cnt] = temp;
}
}
memset(dp,0,sizeof(dp));
ans =dfs(1,cnt,0);
printf("%d\n",ans);
}
return 0;
}
https://cn.vjudge.net/contest/233829#problem/H
题意:
按照一定顺序从左到右覆盖区间,问最少选取几个区间才能覆盖满
分析:
第一感觉就是两个for循环,但是很明显会超时,然后看的题解说用线段树扫描,然后更新。其实就是一个假的dp,一个真的线段树查询和更新
代码:
#include<bits/stdc++.h>
#define ll unsigned long long
#define inf 1e9+7
using namespace std;
int a[500005],b[500005];
struct node
{
int maxn;
}v[50000<<2];
void build(int l,int r,int k)
{
if(l==r)
{
v[k].maxn=inf;
return;
}
int mid;
mid=(l+r)>>1;
build(l,mid,(k<<1));
build(mid+1,r,(k<<1)+1);
v[k].maxn=min(v[(k<<1)].maxn,v[(k<<1)+1].maxn);
}
int sum(int ls,int rs,int l,int r,int k)
{
if(ls<=l&&rs>=r)
return v[k].maxn;
int mid;
mid=(l+r)>>1;
int ans;
ans=inf;
if(rs>mid)
ans=sum(ls,rs,mid+1,r,(k<<1)+1);
if(ls<=mid)
ans=min(ans,sum(ls,rs,l,mid,(k<<1)));
return ans;
}
void update(int pos,int l,int r,int k,int val)
{
if(l==r)
{
v[k].maxn=min(v[k].maxn,val);
return;
}
int mid;
mid=(l+r)>>1;
if(pos<=mid)
update(pos,l,mid,(k<<1),val);
else
update(pos,mid+1,r,(k<<1)+1,val);
v[k].maxn=min(v[(k<<1)].maxn,v[(k<<1)+1].maxn);
return;
}
int main()
{
int i,j,t,n,m;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
build(1,n,1);
update(1,1,n,1,0);
for(i=0;i<m;i++)
{
scanf("%d%d",&a[i],&b[i]);
}
for(i=0;i<m;i++)
{
int ant;
ant=sum(a[i],b[i]-1,1,n,1);
ant=min(ant+1,sum(b[i],b[i],1,n,1));
if(ant==inf)continue;
update(b[i],1,n,1,ant);
}
printf("%d\n",sum(n,n,1,n,1));
if(t)printf("\n");
}
}