XJOI 奋斗群群赛17解题报告
原题
https://vjudge.net/contest/189941 (Codeforces Beta Round #11)
A. Increasing Sequence
题意
给定一个序列,有一种操作可以使序列中某一个数加上d,问最少进行多少次操作使得该序列递增。
题解
对于每一个数,求出进行几次操作能使其大于上一个数并将其更新,最后将总操作次数相加。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2010;
int b[MAXN];
int main(){
int n,d;
int ans=0;
scanf("%d %d",&n,&d);
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
if(i>1&&b[i]<=b[i-1]){
int need=(b[i-1]-b[i])/d+1;
b[i]=b[i]+need*d;
ans+=need;
}
}
printf("%d",ans);
}
B. Jumping Jack
题意
一个人要从0处跳到k处,他第一次跳的距离为1,之后每一次跳的距离加1,跳的方向可以往左或往右,问跳几次可以刚好到达k处
题解
以k为正数为例,向左跳相当于距离减了两倍的跳动距离,因此求出向右跳几次能使距离-k为偶数且距离>k即可,至于具体哪一步是左跳,哪一步是右跳我们无需考虑。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int MAXN=1001000;
int main() {
int tmp;
LL now=0;
LL x;
scanf("%lld",&x);
if(x==0) {
printf("0");
return 0;
}
if(x>0){
for(int i=1; i<=MAXN; i++) {
now+=i;
if(now>=x) {
//cout<<now<<endl;
tmp=i;
break;
}
}
if(now==x) printf("%d",tmp);
else if((now-x)%2==0)printf("%d",tmp);
else {
while((now-x)%2!=0){
tmp++;
now+=tmp;
}
printf("%d",tmp);
}
}
if(x<0){
for(int i=1; i<=MAXN; i++) {
now-=i;
if(now<=x) {
tmp=i;
break;
}
}
if(now==x) printf("%d",tmp);
else if((now-x)%2==0)printf("%d",tmp);
else {
while((now-x)%2!=0){
tmp++;
now+=tmp;
}
printf("%d",tmp);
}
}
}
C-How Many Squares?
题意
给出一个由0,1组成的n*m大小的矩阵,问四边都是数字1的正方形和菱形的数量。
题解
首先对图进行处理,遍历每一个1的点,并对这个点周围的8个点进行扩展(这里用了向量实现),因为我们的遍历有方向性,所以遍历到有效的点时,将它的值改为2,即该点已经遍历。之后计数正方形与菱形。
#include<bits/stdc++.h>
using namespace std;
int dic[8][2]= {{0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
char a[255][255];
int len=0;
int n,m;
void judge(int x,int y) {
if(x<0||x>=n||y<0||y>=m||a[x][y]!='1')return ;
a[x][y]='2';
len++;
for(int i=0; i<8; i++) {
judge(x+dic[i][0],y+dic[i][1]);
}
}
bool solve(int x,int y,int len,int l,int r) {
for(int i=0; i<len+1; i++)
for(int j=l; j<r; j++) {
if(x+dic[j][0]*i<0||x+dic[j][0]*i>=n||y+dic[j][1]*i<0||y+dic[j][1]*i>=m||a[x+dic[j][0]*i][y+dic[j][1]*i]!='2')
return 0;
}
return 1;
}
int main() {
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
for(int i=0; i<n; i++)
scanf("%s",a[i]);
int res=0;
for(int i=0; i<n; i++)
for(int j=0; j<m; j++) {
if(a[i][j]=='1') {
len=0;
judge(i,j);
if(len%4)continue;
res+=solve(i,j,len/4,0,2)&&solve(i+len/4,j+len/4,len/4,2,4);
res+=solve(i,j,len/4,4,6)&&solve(i+len/2,j,len/4,6,8);
}
}
printf("%d\n",res);
}
return 0;
}
D. A Simple Task
题意
给出一副无向图,求环的个数。
题解
刚拿到题目,还以为是用并查集判断环,于是一直没打出来,后来一问原来是状态压缩dp。
为什么用到的是状态压缩dp呢?首先我们注意到点数并不是很多,而题目的路径状态有点类似于TSP问题,因此想到可以dp,二维dp[i][j]表示路径状态为i,路径结尾为j,由于是环,我们定义一个环的结尾为该环中标号最小的点,这样就不会重复计算了,最后由于每个环会算两遍所以答案除以2。
#include<bits/stdc++.h>
using namespace std;
typedef long long int LL;
LL f[1<<20][20],t=0;
int g[20][20],i,j,k,x,y,n,m,q;
int main() {
scanf("%d %d",&n,&m);
while(m--) {
scanf("%d %d",&x,&y);
x--,y--;
g[x][y]=g[y][x]=1;
if(x>y)swap(x,y);
f[(1<<x)+(1<<y)][y]++;
}
for(i=0; i<(1<<n); i++) {
q=1;
for(j=0; j<n; j++)if(((i&(1<<j)))&&(!f[i][j]))if(q)q=0,k=j;
else {
for(y=k+1; y<n; y++)if((i&(1<<y))&&(g[y][j]))f[i][j]+=f[i-(1<<j)][y];
if(g[k][j])t+=f[i][j];
}
}
printf("%lld\n",t/2);
}
总结
思维方面还是欠缺,导致D题一直没想出标算,dp题其实很锻炼思维,需要多加练习,加油!