D - Fence
题意:
K个人对N块木板涂色,每个人初始站在一块木板前(不重复),每人最多只能涂包含所站木板的连续l个木板或一个木板也不涂。给出每人最多涂的木块数l,涂一快木板的工钱p,站的木板s。求这群人最多共获得多少工钱。
题解:dp+单调队列
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示第 i个工人刷到了第 j面墙。
状态转移方程
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
k
]
+
(
j
−
k
)
∗
p
[
i
]
)
;
j
−
l
[
i
]
<
=
k
<
s
[
i
]
;
dp[i][j]=max(dp[i-1][k]+(j-k)*p[i]); j-l[i]<=k<s[i];
dp[i][j]=max(dp[i−1][k]+(j−k)∗p[i]);j−l[i]<=k<s[i];
然后转化为
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
k
]
−
k
∗
p
[
i
]
)
+
j
∗
p
[
i
]
;
dp[i][j]=max(dp[i-1][k]-k*p[i])+j*p[i];
dp[i][j]=max(dp[i−1][k]−k∗p[i])+j∗p[i];
用单调队列维护
m
a
x
(
d
p
[
i
−
1
]
[
k
]
−
k
∗
p
[
i
]
)
max(dp[i-1][k]-k*p[i])
max(dp[i−1][k]−k∗p[i])
注意初始化的位置!!!
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
struct node
{
int l,s,p;
}v[1010],w[1010]; //x表示值,y表示位置 可以理解为下标
bool cmp(node a, node b)
{
return a.s < b.s;
}
int a[MAXN];
int dp[110][MAXN];
int main()
{
int t,n,p,k;
while(scanf("%d%d", &n, &k)!= EOF){
for(int i=1;i<=k;i++)
scanf("%d%d%d",&w[i].l,&w[i].p,&w[i].s);
for(int i=0;i<=k;i++)
for(int j=0;j<=n;j++) dp[i][j]=0;
dp[0][0]=0;
sort(w+1,w+k+1,cmp);
for(int i=1;i<=k;i++)
{
int head=1,tail=0;
for(int j=0;j<=n;j++)
{
if(j>=1)dp[i][j]=max(dp[i-1][j],dp[i][j-1]);///如果第i个工人不用干活
if(j>=max(0,w[i].s-w[i].l)&&j<w[i].s)//先进行初始化
{
while(head<=tail && v[tail].l<=dp[i-1][j]-j*w[i].p) tail--;
v[++tail].l=dp[i-1][j]-j*w[i].p,v[tail].s=j;
}
if(j>=w[i].s&&w[i].s+w[i].l-1>=j)
{
while(head<=tail&&v[head].s+w[i].l<j) head++;
if(head<=tail)
dp[i][j]=max(dp[i][j],v[head].l+w[i].p*j);
}
}
}
printf("%d\n",dp[k][n]);
}
}
C. Maximal GCD
题意:给出数字N和K,要求构造出K个严格递增的数字,是他们的和为N,且这K个数字的最大公因数最大
题解:注意循环的范围,注意特判,剩下的就是简单模拟
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair
const int N=1e6;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
ll mul(ll a,ll b){return (a%mod*b%mod)%mod;}
ll pre(ll a,ll b){return (a%mod/b%mod)%mod;}
ll n,m,k,x,y,z;
int a[N];
int vis[N];
vector<int>p;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n>>m;
ll ans;
if(m%2==0)
ans=m/2*(m+1);
else
ans=(m+1)/2*m;
ll p=0;
if(ans>n||m>141421)//特判,超出数据
{
cout<<-1<<endl;
return 0;
}
if(ans*2>n)
{
p=1;
}
else
for(ll i=1;i*i<=n;i++)//比ans—n的范围快太多......
{
if(n%i==0&&n/i>=ans)///防止溢出
{
p=max(p,i);
}
if(i>=ans&&n%i==0)
{
p=max(p,n/i);
}
}
if(p==0)
cout<<-1<<endl;
else
{
for(ll i=1;i<=m-1;i++)
printf("%lld ",p*i),n-=p*i;
printf("%lld ",n);
}
}
D. Magazine Ad
题意:
给你一段字符串,可以有空格和‘-’来分割,让你确定在分组数目小于等于k的情况下, 最小的分组宽度
题解:二分
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<set>
#include<map>
#include<iterator>
#include<queue>
#include<vector>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
#define mp make_pair
const int N=1e6;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
ll mul(ll a,ll b){return (a%mod*b%mod)%mod;}
ll pre(ll a,ll b){return (a%mod/b%mod)%mod;}
ll n,m,k,x,y,z;
int a[N];
int vis[N];
string str;
int slove(int x,int y)
{
int xx=x,cnt=1;
for(int i=1;i<=y;i++)
{
if(x<a[i]) return 0;
if(xx<a[i])
{
cnt++;
xx=x;
xx-=a[i];
}
else
{
xx-=a[i];
}
}
if(cnt<=n)
return 1;
else
return 0;
}
vector<int>p;
int main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin>>n; getchar();
getline(cin, str);
int cnt=0,ans=0;
int len=str.length();//cout<<str<<endl;
for(int i=0;i<len;i++)
{
if(str[i]=='-'||str[i]==' ')
{
cnt++;
a[cnt]=i-ans+1;
ans=i+1;
}
}
cnt++;
a[cnt]=len-ans;
int l=1,r=len,mid;
int p=0;
while(l<=r)
{
mid=(l+r)/2;
if(slove(mid,cnt))
{
r=mid-1;
p=mid;
}
else
{
l=mid+1;
}
}
cout<<p<<endl;
}
E - Dividing the Path
题意:
有一块长度为
l
l
l的草原,你需要用洒水器把所有草坪都覆盖并且每块草坪只能被覆盖一次,有
n
n
n个奶牛所在的草坪属于
[
l
,
r
]
[l,r]
[l,r],这些区间只能有一个洒水器;
题解:dp+单调队列
动态转移方程:
f
[
i
]
=
m
i
n
(
f
[
j
]
+
1
)
f[i]=min(f[j]+1)
f[i]=min(f[j]+1)
(
A
<
=
(
i
−
j
)
/
2
<
=
B
)
(A<=(i−j)/2<=B)
(A<=(i−j)/2<=B)
初始化时只需要处理
i
−
2
a
i-2a
i−2a 处的
d
p
dp
dp 值即可,因为要遍历
1
−
m
1-m
1−m,
所以处理过的
i
−
2
a
=
j
−
2
b
i-2a=j-2b
i−2a=j−2b,所以 vis[i] 要在初始化后判断
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
struct node
{
int l,s,p;
}v[MAXN];
bool cmp(node a, node b)
{
if(a.l==b.l)
return a.s > b.s;
return a.l<b.l;
}
int vis[MAXN];
int dp[MAXN];
///f[i]=min(f[j]+1)(A<=(i−j)/2<=B)
int main()
{
int t,n,m,p,k,a,b;
scanf("%d%d",&n,&m);
scanf("%d%d",&a,&b);
for(int i=1;i<=n;i++)//标记不能作为左右端点的坐标
{
scanf("%d%d",&p,&k);
for(int i=p+1;i<=k-1;i++)
vis[i]=1;
}
memset(dp,INF,sizeof(dp));
dp[0]=0;
int head=1,tail=0,j=0;
for(int i=2*a;i<=m;i+=2)
{
j=i-2*a;
while(head<=tail && v[tail].l>=dp[j]&&vis[j]!=1) tail--;//初始化
v[++tail].l=dp[j],v[tail].s=j;
if(vis[i]) continue;
while(head<=tail&&i-v[head].s>2*b) head++;//(i-2b,i-2a)范围内的单调队列
if(head<=tail)
dp[i]=min(dp[i],v[head].l+1);
}
if(dp[m]!=INF)printf("%d\n",dp[m]);
else printf("-1\n");
}
当只涉及 一个状态转移方程时可以不用加 m i n / m a x min/max min/max
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int MAXN=1e6+10;
const int INF=0x3f3f3f3f;
struct node
{
int l,s,p;
}v[MAXN];
bool cmp(node a, node b)
{
if(a.l==b.l)
return a.s > b.s;
return a.l<b.l;
}
int vis[MAXN];
int dp[MAXN];
///f[i]=min(f[j]+1)(A<=(i−j)/2<=B)
int main()
{
int t,n,m,p,k,a,b;
scanf("%d%d",&n,&m);
scanf("%d%d",&a,&b);
for(int i=1;i<=n;i++)//标记不能作为左右端点的坐标
{
scanf("%d%d",&p,&k);
for(int i=p+1;i<=k-1;i++)
vis[i]=1;
}
memset(dp,INF,sizeof(dp));
dp[0]=0;
int head=1,tail=0,j=0;
for(int i=2*a;i<=m;i+=2)
{
j=i-2*a;
while(head<=tail && v[tail].l>=dp[j]&&vis[j]!=1) tail--;//初始化
v[++tail].l=dp[j],v[tail].s=j;
if(vis[i]) continue;
while(head<=tail&&i-v[head].s>2*b) head++;//(i-2b,i-2a)范围内的单调队列
if(head<=tail)
dp[i]=v[head].l+1;
}
if(dp[m]<=INF)printf("%d\n",dp[m]);
else printf("-1\n");
}
E. Roma and Poker
题意:
有
n
n
n场比赛,给出一个
k
k
k值,每场比赛的结果用W表示胜,L表示败,D表示平, ? 表示未知,?处可以自定义 胜负平
问是否有一个序列满足以下2个要求,有则输出序列,无则输出NO
设前i场胜
W
[
i
]
W[i]
W[i],负
L
[
i
]
L[i]
L[i]
1、前n-1场中,不能有
∣
W
[
i
]
−
L
[
i
]
∣
>
=
k
|W[i]-L[i]|>=k
∣W[i]−L[i]∣>=k
2、最后一场,
∣
W
[
n
]
−
L
[
n
]
∣
=
k
|W[n]-L[n]|=k
∣W[n]−L[n]∣=k
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前i场
W
[
i
]
−
L
[
i
]
W[i]-L[i]
W[i]−L[i]的差为
j
j
j
题解:DP,按题意模拟所有状态,如果最终状态满足条件,就回溯回去
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=1e7+10;
const int MAXN=2010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
int n,m,k,x,y,t;
const int Z=1005;
char s[MAXN];
int dp[MAXN][MAXN];
int pre[MAXN][MAXN];
int main()
{
scanf("%d%d %s", &n, &k, s);
dp[0][Z] = 1;
for (int i=0;i<n;i++)
for(int j=-k+1;j<k;j++)
{
if (!dp[i][Z+j]) continue;
if(s[i]=='W'||s[i]=='?')
{
dp[i+1][Z+j+1]=1;
pre[i+1][Z+j+1]=1;
}
if(s[i]=='D'||s[i]=='?')
{
dp[i+1][Z+j]=1;
pre[i+1][Z+j]=0;
}
if(s[i]=='L'||s[i]=='?')
{
dp[i+1][Z+j-1]=1;
pre[i+1][Z+j-1]=-1;
}
}
if (dp[n][Z+k])
{
int ans=n,sum=Z+k;
while(ans)
{
int temp=pre[ans][sum];
ans--;
sum-=temp;
if(temp==1)
{
s[ans]='W';
}
if(temp==0)
{
s[ans]='D';
}
if(temp==-1)
{
s[ans]='L';
}
}
printf("%s\n",s);
}
else if (dp[n][Z-k])
{
int ans=n,sum=Z-k;
while(ans)
{
int temp=pre[ans][sum];
ans--;
sum-=temp;
if(temp==1)
{
s[ans]='W';
}
if(temp==0)
{
s[ans]='D';
}
if(temp==-1)
{
s[ans]='L';
}
}
printf("%s\n",s);
}
else
printf("NO\n");
}