D. Constructing the Array
题目链接:点击查看
题目描述:
给一个n个0的数组,每次都去寻找最左边的最长连续0子段的中间位置进行赋值。
题目分析:
可以使用优先队列进行解决,每次都是将node(l,r)添加进去,每次取出来的都是连续0长度最长或者是最左边的段。
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[200005];
struct node{
int l;
int r;
node(int x,int y){
l=x;
r=y;
}
bool operator < (const node & temp)const{
if(r-l==temp.r-temp.l){
return temp.l < l;//相等长度,左边优先
}
return temp.r-temp.l>r-l;
}
};
priority_queue<node> q;
void bfs(){
int cnt=0;
q.push(node(1,n));
while(!q.empty()){
node temp = q.top();
q.pop();
if(temp.l>temp.r)continue;
int mid = (temp.l+temp.r)>>1;
a[mid]=++cnt;
q.push(node(temp.l,mid-1));
q.push(node(mid+1,temp.r));
}
}
int main()
{
int t;
cin >> t;
while(t--){
scanf("%d",&n);
bfs();
for(int i = 1; i <= n; ++i){
printf("%d ",a[i]);
}
cout << endl;
}
return 0;
}
E. K-periodic Garland
题目链接:点击查看
题目描述:
给一个01字符串,每次操作改变一个字符状态,使得字符串中1的相邻距离为k,问最少操作数。
题目分析:
可以使用dp进行解决。
sum[i]:记录前i项中1的个数。
dp[i][0]表示前i项都正确,第i位为0时的最小操作次数。
dp[i][1]表示前i项都正确,第i位为1时的最小操作次数。
转移方程:
- dp[i][0]=min(dp[i−1][0],dp[i−1][1])+(s[i]==‘1’)
当前记录第i项为0时的状态,那么此处为0对前面是否正确不影响,可以直接
从dp[i−1][0]或者dp[i−1][1]转移过来,如果s[i]==‘1’,那么操作数加1。
- dp[i][1]=min(dp[i−k][1]+sum[i−1]−sum[i−k],sum[i−1])+(s[i]==‘0’)
当前记录第i项为1时的状态,如果前i−k项合法时,要使第i项为1时合法,需要我们使第i−k+1项到第i−1项都为0,第i项才能为1,这也就是为什么要记录前缀和1的个数。或者让前i-1项数都为0,这种情况是sum[i-1]。然后比较取小值。最后操作数会根据s[i]=='0’继续加1。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,k;
int sum[1000005];
char s[1000005];
int dp[1000005][2];
int main()
{
int t;
cin >> t;
while(t--){
cin >> n >> k; //scanf输入,下面输入字符串很难受
cin >> s+1;
for(int i = 1; i <= n; ++i){
sum[i]=sum[i-1]+(s[i]=='1');
}
int temp;
for(int i = 1; i <= n; ++i){
temp=max(0,i-k);//需要注意i-k
dp[i][0]=min(dp[i-1][0],dp[i-1][1])+(s[i]=='1');
dp[i][1]=min(dp[temp][1]+sum[i-1]-sum[temp],sum[i-1])
+(s[i]=='0');
}
cout << min(dp[n][0],dp[n][1]) << endl;
for(int i = 0; i <= n; ++i){
dp[i][0]=0,dp[i][1]=0,sum[i]=0;//别忘了初始化
}
}
return 0;
}