预计分数:100+20+50=first
实际分数:20+0+10=gg
水灾(sliker.cpp/c/pas) 1000MS 64MB
大雨应经下了几天雨,却还是没有停的样子。土豪CCY刚从外地赚完1e元回来,知道不久除了自己别墅,其他的地方都将会被洪水淹没。
CCY所在的城市可以用一个N*M(N,M<=50)的地图表示,地图上有五种符号:“. * X D S”。其中“X”表示石头,水和人都不能从上面经过。“.”表示平原,CCY和洪水都可以经过。“*”表示洪水开始地方(可能有多个地方开始发生洪水)。“D”表示CCY的别墅。“S”表示CCY现在的位置。
CCY每分钟可以向相邻位置移动,而洪水将会在CCY移动之后把相邻的没有的土地淹没(从已淹没的土地)。
求CCY回到别墅的最少时间。如果聪哥回不了家,就很可能会被淹死,那么他就要膜拜黄金大神涨RP来呼叫直升飞机,所以输出“ORZ hzwer!!!”。
输入文件 sliker.in
输出文件 sliker.out
Input
3 3
D.*
…
.S.
Output
3
Input
3 3
D.*
…
..S
Output
ORZ hzwer!!!
Input
3 6
D…*.
.X.X..
….S.
Output
6
做这道题的时候可以说思路来的还是非常快的,一眼就看出是BFS暴力,但是可能是太大意了吧,把洪水覆盖的vis数组误以为是人走过的
vis数组,so没了判重就gg了,不过我还是很好奇为什么样例都能过而且还能拿两个点。。。。后来拿到代码改了一下死活是90分,,其实
现在想想还是数据太弱了,如果出题人狠的话估计真的能把我卡到爆零无话可说。看了n遍之后发现一个细节就是没有考虑洪水把当前正在搜索的节点淹了的情况,但是怎么改呢?其实我在洪水扩展之前已经判断过一次了。然后把前面的复制了一遍发现不对,后来把pop删掉就对了。。也是比较mengbi
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
int n,m;
int xx[]={-,+,,};
int yy[]={,,-,+};
struct peo
{
int juli;//
int x,y;
int step;
}now,nxt;
int map[][];
int bgx,bgy,homex,homey;
int vis[][];// 被洪水淹没的地方,注意要map和vis同时判断
int ans=;
int watercishu=;
int flag=;
int vis2[][];
int calca(int xxx,int yyy)
{
return max(xxx,homex)-min(xxx,homex)+max(yyy,homey)-min(yyy,homey);
}
void init()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
char p;
cin>>p;
if(p=='.')
map[i][j]=;// 都可以通过
else if(p=='X')
map[i][j]=;// 都不可以通过
else if(p=='S')
{map[i][j]=;//人现在的位置
bgx=i;bgy=j;}
else if(p=='*')
map[i][j]=,vis[i][j]=;// 洪水开始的地方
else if(p=='D')
{
map[i][j]=;// 家
homex=i;
homey=j;
} }
}
void ex()
{
int flag=;
for(int i=;i<=n;i++)
{
for(int j=;j<=m;j++)
{
if(vis[i][j]==watercishu)
{
for(int k=;k<;k++)
{
int wx=i+xx[k];
int wy=j+yy[k];
if(vis[wx][wy]==&&map[wx][wy]!=&&map[wx][wy]!=&&wx>=&&wx<=n&&wy>=&&wy<=m)
{
vis[wx][wy]=vis[i][j]+;
}
}
}
}
}
watercishu++;
}
void bfs()
{
queue<peo>q;
now.x=bgx;now.y=bgy;now.step=;now.juli=calca(bgx,bgy);
q.push(now);
int last=;// 记录上一次洪水扩展时人走的步数
while(q.size()!=)
{
peo p=q.front();
if(vis[p.x][p.y]!=)
{
q.pop();
continue;
}
if(p.juli==)
{
printf("%d",p.step);
flag=;
return ;
} q.pop();
if(p.step>last)
{
ex();// 洪水扩展
last=p.step;
}
if(vis[p.x][p.y]!=)
{
continue;
}
for(int i=;i<;i++)
{
int wx=p.x+xx[i];
int wy=p.y+yy[i];
if(vis2[wx][wy]==&&vis[wx][wy]==&&map[wx][wy]!=&&wx>=&&wx<=n&&wy>=&&wy<=m)
{
vis2[wx][wy]=;
nxt.x=wx;
nxt.y=wy;
nxt.step=p.step+;
nxt.juli=calca(wx,wy);
q.push(nxt);
}
} }
}
int main()
{
freopen("sliker.in","r",stdin);
freopen("sliker.out","w",stdout);
init();
bfs();
if(flag==)
printf("ORZ hzwer!!!");
return ;
}
不说什么
某种数列问题 (jx.cpp/c/pas) 1000MS 256MB
众所周知,chenzeyu97有无数的妹子(阿掉!>_<),而且他还有很多恶趣味的问题,继上次纠结于一排妹子的排法以后,今天他有非(chi)常(bao)认(cheng)真(zhe)去研究一个奇怪的问题。有一堆他的妹子站成一排,然后对于每个妹子有一个美丽度,当然美丽度越大越好,chenzeyu97妹子很多,但是质量上不容乐观,经常出现很多美丽度为负数的妹子(喜闻乐见),chenzeyu97希望从一排妹子里找出3队连续的妹子,使她们的美丽度和最大。注意,一个妹子不能被编入多个队伍而且一定要拿出三队,不然czy会闲着没事做~。
简单滴说就是:
给定一个数列,从中找到3个无交集的连续子数列使其和最大。
【输入文件】
第一行一个数n,表示数列长度。
接下来有n行,每行一个数,第i行为第i个数。
【输出文件】
仅有一个数,表示最大和。
【样例输入】 jx.in
10
-1
2
3
-4
0
1
-6
-1
1
-2
【样例输出】 jx.out
7
【样例说明】
第一队妹子取2,3。
第二队妹子取0,1。
第三队妹子取1。
【数据范围】
请大家放心,虽然chenzeyu97妹子无数,但是这次他叫来的个数n是有限的。=v=
对于30%的数据,妹子数不大于200。
对于60%的数据,妹子数不大于2000。
对于100%的数据,妹子数1000000。
而且,由于chenzeyu97没有CCR那样的影响力,所以他的妹子选完的最大美丽度和不超过maxlongint。(注:CCR随便选就爆long long,因为他是把妹狂魔=V=)。
考场上天真的以为只要把所有的正数序列找到然后排一遍序就可以了,但是现在想想好智障啊,而且问了问同学之后发现智障的还不止是我一个。=.=
错误思路:寻找所有正数序列并排序
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
LL n;
LL a[];
struct node
{
LL id;
LL yanzhi;
LL js;
}xulie[];
LL num=;
LL vis[];
LL compxiaoyu(const LL a,const LL b)
{
return a>b;
}
LL compdayu(const node & a ,const node & b)
{
return a.yanzhi>b.yanzhi;
}
int main()
{
freopen("jx.in","r",stdin);
freopen("jx.out","w",stdout);
//ios::sync_with_stdio(false);
cin>>n;
for(LL i=;i<=n;i++)
cin>>a[i];
for(LL i=;i<=n;i++)
{
if(a[i]>=&&vis[i]==)
{
xulie[++num].id=i;
xulie[num].js=;
for(LL j=i;a[j]>=&&j<=n;j++)
{
vis[j]=;
xulie[num].yanzhi+=a[j];
xulie[num].js++;
}
}
}
/*for(LL i=1;i<=num;i++)
cout<<xulie[i].yanzhi<<endl;*/
if(num<)
{
LL ans=;
LL zssl=;
for(LL i=;i<=num;i++)
{
zssl+=xulie[i].js;
}
if(zssl>=)
{
for(LL i=;i<=num;i++)
{
ans+=xulie[i].yanzhi;
}
//printf("%d",ans);
cout<<ans;
}
else
{
sort(a+,a+n+,compxiaoyu);
num=zssl;
for(LL i=;i<=n;i++)
{
if(a[i]<)
{
xulie[++num].yanzhi=a[i];
if(num==)
break;
}
}
for(LL i=;i<=;i++)
{
ans+=xulie[i].yanzhi;
}
//printf("%d",ans);
cout<<ans;
} }
else if(num==)
{
LL ans=;
for(LL i=;i<=;i++)
ans+=xulie[i].yanzhi;
cout<<ans;
}
else if(num>)
{
LL ans=;
sort(xulie+,xulie+num+,compdayu);
for(LL i=;i<=;i++)
{
ans+=xulie[i].yanzhi;
}
cout<<ans;
}
return ;
}
爆零
正确思路:dp
用dp[i][j][0]表示前i个点,取了j段,第i个点没有取的最大值
用dp[i][j][1]表示前i个点,取了j段,第i个点取了的最大值
答案=max(dp[n][3][0],dp[n][3][1])
转移的时候考虑取第i个和不取i个两种情况
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int a[];
int dp[][][];
int main()
{
int n;
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&a[i]); for(int i=;i<=n;i++)
{
for(int j=;j<=;j++)
{
dp[i][j][]=max(dp[i-][j][],dp[i-][j][]);
dp[i][j][]=max(dp[i][j][],dp[i-][j-][]+a[i]);
dp[i][j][]=max(dp[i][j][],dp[i-][j][]+a[i]);
}
}
printf("%d",max(dp[n][][],dp[n][][]));
return ;
}
AC
密码锁 1000MS 512MB
Input: password.in
Output: password.out
【题目描述】
hzwer有一把密码锁,由N个开关组成。一开始的时候,所有开关都是关上的。当且仅当开关x1,x2,x3,...xk为开,其他开关为关时,密码锁才会打开。
他可以进行M种的操作,每种操作有一个size[i],表示,假如他选择了第i种的操作的话,他可以任意选择连续的size[i]个格子,把它们全部取反。(注意,由于黄金大神非常的神,所以操作次数可以无限>_<)
本来这是一个无关紧要的问题,但是,黄金大神不小心他的钱丢进去了,没有的钱他哪里能逃过被chenzeyu97 NTR的命运?>_< 于是,他为了虐爆czy,也为了去泡更多的妹子,决定打开这把锁。但是他那么神的人根本不屑这种”水题”。于是,他找到了你。
你的任务很简单,求出最少需要多少步才能打开密码锁,或者如果无解的话,请输出-1。
【输入格式】
第1行,三个正整数N,K,M,如题目所述。
第2行,K个正整数,表示开关x1,x2,x3..xk必须为开,保证x两两不同。
第三行,M个正整数,表示size[i],size[]可能有重复元素。
【输出格式】
输出答案,无解输出-1。
【样例输入1】
10 8 2
1 2 3 5 6 7 8 9
3 5
【样例输出1】
2
【样例输入2】
3 2 1
1 2
3
【样例输出2】
-1
【数据规模】
对于50%的数据,1≤N≤20,1≤k≤5,1≤m≤3;
对于另外20%的数据,1≤N≤10000,1≤k≤5,1≤m≤30;
对于100%的数据,1≤N≤10000,1≤k≤10,1≤m≤100。
这道题。。一看就是一道非常高端大气上档次的题目。一开始我还想用KMP,但是发现这道题的重点并不是KMP。看了一眼数据范围发现50%的n是二十,,,二十??暴力!!!于是乎便开始搞BFS+map判重。跑了一下样例能过,自己造的极端数据也能过,-1的情况也考虑到了,心想怎么着也得搞个三四十吧,,但是很遗憾。。出题人也是比较言(lao)而(jian)有(ju)信(huan),前五个数据居然除了第一个点的n==4之外其他的n all == 20.。。。。。这么说来我写了130行的BFS和输出-1有什么区别????!!!!!
丢分原因:1.能力不够
2.外界因素
正确思路:
对于区间取反 复杂度是O(n)
但是因为这题序列只有01
如果操作查分序列的话就快多了
先搞出查分序列 然后bfs求出每两个点的1相互抵消最少操作次数
因为最后的序列最多20个1 所以状丫dp搞一搞求出min操作次数 注意到题目中的是区间修改,把沿途的位置取反,
这个可以看做是在模2意义下,给区间的加一操作。在我们通常的思路中,对于区间的操作,原本是要修改区间长度个的位置的情况,我们都可以通过考虑它的差分序列,使得要修改的位置个数变成2个,我们要求最少的修改,使得原序列变成全0。 所以对原序列进行差分,那么每次修改就是要你对i号位置和i+size[]模2意义下的加1。 差分后的序列中,数值为1的个数是不会超过2k个,即不会超过20个。 考虑每次对i和i+x改动的过程,如果原序列中,
i号位置和i+x号位置都是0的话,我们这么改,没有任何必要。
所以任意时刻,数值为1的位置个数是不会增加的,那么我们可以把每一个的1看成一个的石子,
那么每次我们可以把石子往某个方向移动size[]步,如果移动之后的位置存在石子的话,就对对碰,消掉了。 因为是对对碰,石子之间的关系肯定是一个匹配的关系,我们不妨求出Dist[i][j]表示,
石子i要走到石子j的位置,至少需要移动多少步,那么我们可以枚举每一个石子,
然后进行一遍的bfs即可,这一部分的复杂度是O(2kmn)。
现在问题转化为有一个大小不超过20的完全图,我们想要求它的最小权最大匹配。
暴力代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
int n,k,m;// 数字的个数 , 需要打开的个数,操作的个数
int ned[];// 需要打开的数字
int kong[];
int what[];// 各种走法
int flag=,ans=-;
map<string,int>mp;
struct node
{
int nowing[];// 已经得到的答案
int step;// 步数
}now,nxt;
void makeans()
{
string ans;
for(int i=;i<=n;i++)
ans=ans+(char)(ned[i]+);
mp[ans]=;
//cout<<ans<<endl;
}
int pd(node how)
{
string w;
for(int i=;i<=n;i++)
w=w+(char)(how.nowing[i]+);
if(mp[w]==)
{
ans=how.step;
flag=;
return ;
}
if(mp[w]==)
{
mp[w]=;
return ;
}
} void bfs()
{
for(int i=;i<=n;i++)
now.nowing[i]=kong[i];
now.step=;
queue<node>q;
q.push(now);
while(q.size()!=)
{
node p=q.front();
q.pop();
for(int i=;i<=n;i++)// 对每一位进行操作
{
for(int j=;j<=m;j++)// 每一种操作
{
if(what[j]+i<=n)
{
memset(nxt.nowing,,sizeof(nxt.nowing));
for(int k=;k<=n;k++)
nxt.nowing[k]=p.nowing[k];
for(int k=i;k<=i+what[j]-;k++)
{
if(nxt.nowing[k]==)
nxt.nowing[k]=;
else
nxt.nowing[k]=;
nxt.step=p.step+; }
if(pd(nxt)==)// 没有出现过
{
if(flag==)
{
printf("%d",ans);
break;
}
q.push(nxt);
}
}
if(flag==)
break;
}
if(flag==)
break;
}
if(flag==)
break;
}
}
int main()
{
freopen("password.in","r",stdin);
freopen("password.out","w",stdout);
scanf("%d%d%d",&n,&k,&m);
for(int i=;i<=k;i++)
{
int p;
scanf("%d",&p);
ned[p]=;
}
for(int i=;i<=m;i++)
scanf("%d",&what[i]);
makeans(); bfs(); if(flag==)
{
printf("-1");
} return ;
}
10分的暴力
正确代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 10010
using namespace std;
int n,m,k,size[],a[maxn],c[maxn],s[maxn],cnt,step[][];
int f[maxn],dis[maxn],dp[<<],inf;
struct node
{
int x,t;
};
queue<node>q;
void Bfs(int S,int o)
{
while(!q.empty())q.pop();
memset(f,,sizeof(f));
memset(dis,/,sizeof(dis));
inf=dis[];
q.push((node)
{
S,
});
f[S]=;
dis[S]=;
while(!q.empty())
{
node p=q.front();
q.pop();
for(int i=; i<=k; i++)
{
int y=p.x+size[i];
if(y<=n&&f[y]==)
{
f[y]=;
dis[y]=p.t+;
q.push((node)
{
y,dis[y]
});
}
y=p.x-size[i];
if(y>=&&f[y]==)
{
f[y]=;
dis[y]=p.t+;
q.push((node)
{
y,dis[y]
});
}
}
}
for(int i=; i<=cnt; i++)
if(dis[c[i]]<inf)step[o][i]=dis[c[i]];
else step[o][i]=inf;
}
int main()
{
// freopen("password.in","r",stdin);
// freopen("password.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=; i<=m; i++)
{
int x;
scanf("%d",&x);
a[x]++;
}
for(int i=; i<=k; i++)
scanf("%d",&size[i]);
n++;
for(int i=; i<=n; i++)
s[i]=a[i]-a[i-];
for(int i=; i<=n; i++)
if(s[i])
c[++cnt]=i;
for(int i=; i<=cnt; i++)
Bfs(c[i],i);
memset(dp,/,sizeof(dp));
inf=dp[];
dp[]=;
for(int i=; i<=(<<cnt)-; i++)
{
int j;
for(int k=; k<=cnt; k++)
if((<<k-)&i)
{
j=k;
break;
}
for(int k=; k<=cnt; k++)
if((<<k-)&i)
dp[i]=min(dp[i],dp[i^(<<j-)^(<<k-)]+step[j][k]);
}
if(dp[(<<cnt)-]==inf)printf("-1\n");
else printf("%d\n",dp[(<<cnt)-]);
return ;
}
BFS+状压DP-AC
/*
将每个点拆成俩个点x,x’,能匹配的点xy从x向y’
连边权值为1费用 Dist[i][j],源点向x连边,x’向汇连边,都是权值1 费用0,然后一遍 最小费用最大流就可以出解
*/
#include <cstring>
#include <cstdio>
#include <queue> #define Max 10005
#define INF 1e8 namespace Z
{
void read (int &now)
{
now = ;
register char word = getchar ();
while (word < '' || word > '')
word = getchar ();
while (word >= '' && word <= '')
{
now = now * + word - '';
word = getchar ();
}
} inline int min (const int &_Qos_swc, const int &_Cos_ses)
{
return _Qos_swc < _Cos_ses ? _Qos_swc : _Cos_ses;
}
} int S, T = ;
int M, N, K;
int key[Max];
int size[Max];
int Answer;
int Count; int date[Max];
int number[Max]; int cost[Max / ][Max / ]; bool visit[Max];
int dis[Max]; void Bfs (int res)
{
memset (visit, false, sizeof (visit));
std :: queue <int> Queue;
visit[res] = true;
dis[res] = ;
Queue.push (res);
int now;
while (!Queue.empty ())
{
now = Queue.front ();
Queue.pop ();
for (int i = ; i <= M; i++)
{
if (now + size[i] <= N && (!visit[size[i] + now]))
{
visit[size[i] + now] = true;
dis[now + size[i]] = dis[now] + ;
Queue.push (now + size[i]);
}
if (now - size[i] > && (!visit[now - size[i]]))
{
visit[now - size[i]] = true;
dis[now - size[i]] = dis[now] + ;
Queue.push (now - size[i]);
}
}
}
} class Net_Flow_Type
{
private : struct Edge_Date
{
int from;
int to;
int flow;
int next;
int cost;
}
edge[Max << ]; int Edge_Count;
int edge_list[Max];
int deep[Max];
int pre[Max]; public : void Prepare ()
{
Edge_Count = ;
} inline void AddEdge (int from, int to, int flow, int cost)
{
Edge_Count++;
edge[Edge_Count].from = from;
edge[Edge_Count].to = to;
edge[Edge_Count].flow = flow;
edge[Edge_Count].cost = cost;
edge[Edge_Count].next = edge_list[from];
edge_list[from] = Edge_Count;
Edge_Count++;
edge[Edge_Count].from = to;
edge[Edge_Count].to = from;
edge[Edge_Count].flow = ;
edge[Edge_Count].cost = -cost;
edge[Edge_Count].next = edge_list[to];
edge_list[to] = Edge_Count;
} bool Spfa ()
{
for (int i = ; i <= T; i++)
deep[i] = INF;
std :: queue <int> Queue;
memset (visit, false, sizeof visit);
Queue.push (S);
deep[S] = ;
visit[S] = true;
int now;
while (!Queue.empty ())
{
now = Queue.front ();
Queue.pop ();
for (int i = edge_list[now]; i; i = edge[i].next)
if (edge[i].flow && deep[now] + edge[i].cost < deep[edge[i].to])
{
deep[edge[i].to] = deep[now] + edge[i].cost;
pre[edge[i].to] = i;
if (!visit[edge[i].to])
{
visit[edge[i].to] = true;
Queue.push (edge[i].to);
}
}
visit[now] = false;
}
return deep[T] < INF;
} void Dinic ()
{
while (Spfa ())
{
int res = INF;
for (int i = pre[T]; i; i = pre[edge[i].from])
res = Z :: min (res, edge[i].flow);
for (int i = pre[T]; i; i = pre[edge[i].from])
{
edge[i].flow -= res;
edge[i ^ ].flow += res;
Answer += edge[i].cost * res;
}
}
} void Insert_Edges ()
{
int o = ;
for (int i = ; i <= Count; i++)
{
AddEdge (S, i, , );
AddEdge (i + Count, T, , );
}
for (int i = ; i <= Count; i++)
for (int j = ; j <= Count; j++)
if (i != j && cost[i][j] != INF)
AddEdge (i, j + Count, , cost[i][j]);
// for (int i = 1; i <= Edge_Count; i++)
// printf ("%d %d %d %d %d \n", edge[i].from, edge[i].to, edge[i].flow, edge[i].cost, edge[i].next);
} }; Net_Flow_Type Make; int main (int argc, char *argv[])
{
freopen ("password.in", "r", stdin);
freopen ("password.out", "w", stdout);
Z :: read (N);
Z :: read (K);
Z :: read (M);
N++;
Make.Prepare ();
for (int i = ; i <= K; i++)
{
Z :: read (key[i]);
date[key[i]] = ;
}
for (int i = ; i <= M; i++)
Z :: read (size[i]);
for (int i = N; i; i--)
date[i] ^= date[i - ];
for (int i = ; i <= N; i++)
if (date[i])
number[i] = ++Count;
for (int i = ; i <= N; i++)
if (date[i])
{
Bfs (i);
for (int j = ; j <= N; j++)
{
if (!number[j])
continue;
if (!visit[j])
cost[number[i]][number[j]] = INF;
else
cost[number[i]][number[j]] = dis[j];
}
}
Make.Insert_Edges ();
Make.Dinic ();
printf ("%d", (Answer >> ));
return ;
}
90分最小费用最大流
总结:
这次考试如果没有T1的话考的还算是非常好的。
主要是这次考试之后我发现了很多事情
一.自己的优点:
1.思路来的比较快,但是有些思路是错的,经不起推敲,但是有时候错的思路能为正确的思路奠定基础
(T1是这样,不啰嗦了)
2.coding代码的能力比较强,特别是在调试方面,T1大部分同学都用了一个半小时到三小时不等,但是我用了不到一个小时(虽然有点小错误)。三道题加起来代码写了接近380行。。也算是奇迹
二.自己的缺点
细节!细节!细节!,细节决定成败,这句话在oi的赛场上体现的淋漓尽致。以后在做题的时候感觉可以找一下在乒乓球赛场上的感觉(我在乒乓球赛场上稳如泰山,全县rank1就是靠这个拿的)。相信我的oi赛场和乒乓球赛场肯定有很多可以互补的地方
三.考试经验
1.在做搜索题目的时候,必须要自己手动生产一下比较简单但是数据规模比较大的数据。(毕竟每道题都有几个这样的测试点)
2.最后一题其实我可以拿到20分的,怎么搞呢?只要在搜索里面加个计数,当这个数非常大的时候退出就可以了
That all
加油!