上一场自己状态爆表;这一场队友爆表,自己捡表了;题目呢,总的来说不难,相比于前面几场比赛来说,关键在于如何建图。
像D题,在我的上一个博客里面就是一道同理的题,只需要求入度为0和出度为0的点最大数,然后特判已经强连通(比如只有一个点)的情况。
可以看看我上一个博客,可能会对此比赛有帮助。
【A 石油采集】
题解:二分匹配之匈牙利。
【B 道路建设】
题解:并查集。
【C 求交集】
题解:双指针扫一遍即可。
【D 小明的挖矿之旅】
题解:求度为0的点数量。
【E 通知小弟】
题解:处理入度为0的新点;
【F Call to your teacher】
题解:水
【G 老子的意大利炮呢】
题解:分层+优先队列SPFA。
【H 老子的全排列呢】
题解:STL之permutation或者试一试康拓展开。
-------------------------------代码-----------------------------
【A】
12月份刚好看过队友做过此题,当然那个题我还懵b了,所以记得。
可以先去看看POJ 2446 ,POJ3020。附上队友博客:LZH


#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=0,ff=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();}
return xx*ff;
}
const int ws_[4]={0,1,0,-1},ad_[4]={1,0,-1,0};
int N,cas=0;
char mp[55][55];
inline int id(int x,int y)
{return (x-1)*N+y;}
int lin[3100],len;
struct edge{
int y,next;
}e[100010];
inline void insert(int xx,int yy){
e[++len].next=lin[xx];
lin[xx]=len;
e[len].y=yy;
}
inline void ins(int xx,int yy)
{insert(xx,yy),insert(yy,xx);}
bool vis[3100];
int match[3100];
bool hun(int x){
for(int i=lin[x];i;i=e[i].next)
if(!vis[e[i].y]){
vis[e[i].y]=1;
if(match[e[i].y]==0||hun(match[e[i].y])){
match[e[i].y]=x;
match[x]=e[i].y;
return 1;
}
}
return 0;
}
int main(){
//freopen("in","r",stdin);
for(int T=read();T;T--){
printf("Case %d: ",++cas);
char ch;
N=read();
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++){
ch=getchar();
while(ch==' '||ch=='\n')
ch=getchar();
mp[i][j]=ch;
}
memset(lin,0,sizeof(lin));len=0;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(mp[i][j]=='#')
for(int k=0;k<=3;k++){
int tx=i+ws_[k],ty=j+ad_[k];
if(tx<=0||ty<=0||tx>N||ty>N)
continue;
if(mp[tx][ty]=='#')
ins(id(i,j),id(tx,ty));
}
memset(match,0,sizeof(match));
int ans=0;
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(mp[i][j]=='#'){
if(!match[id(i,j)]){
memset(vis,0,sizeof(vis));
if(hun(id(i,j)))
ans++;
}
}
printf("%d\n",ans);
}
}
【B】
并查集


#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=0,ff=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();}
return xx*ff;
}
int C,N,M;
struct edge{
int x,y,v;
bool friend operator<(const edge&A,const edge&B)
{return A.v<B.v;}
}e[10010];
int father[110];
int getfather(int x){
if(father[x]==x)
return x;
return father[x]=getfather(father[x]);
}
int main(){
//freopen("in","r",stdin);
while(scanf("%d",&C)!=EOF){
N=read(),M=read();
for(int i=1;i<=N;i++)
e[i].x=read(),e[i].y=read(),e[i].v=read();
sort(e+1,e+1+N);
for(int i=1;i<=M;i++)
father[i]=i;
int cnt=0;
long long ans=0;
for(int i=1;i<=N;i++){
int f1=getfather(e[i].x),f2=getfather(e[i].y);
if(f1==f2)
continue;
cnt++;
ans+=e[i].v;
father[f2]=f1;
if(cnt==M-1)
break;
}
if(ans<=C)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}
【C】
由于已经有序,而且集合具有互异性,可以用双指针。
假设两个队伍,每次比较队首,小的一方后移队首。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=0,ff=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();}
return xx*ff;
}
const int maxn=1000010;
int a[maxn],b[maxn],N,M;
int ans[maxn],tp;
int main(){
while(scanf("%d %d",&N,&M)!=EOF){
for(int i=1;i<=N;i++)
a[i]=read();
for(int i=1;i<=M;i++)
b[i]=read();
int i=1,j=1,tp=0;
while(i<=N&&j<=M){
if(a[i]==b[j])
ans[++tp]=a[i],i++,j++;
else if(a[i]>b[j])
j++;
else
i++;
}
if(tp==0) printf("empty\n");
else {
for(int k=1;k<tp;k++)
printf("%d ",ans[k]);
printf("%d\n",ans[tp]);
}
}
return 0;
}
【D】
就是问有向图至少加几条有向边使得图成为强连通,结论是:当已经强连通时,答案为0(此情况就是所谓的特判);否则,答案=max(出度为0数,入度为0数)。
对于上面的结论,我以前证明过。但是找了下没找到对应的博客。
Tarjan缩点建图,求度为0的点数。我的代码如下,但可能数据有点大,只通过了87%。


//有向图缩点 ,注意scc_cnt=1时。
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1000010;
int Laxt[maxn],Next[maxn<<1],To[maxn<<1],cnt,n,m;
int dfn[maxn],low[maxn],times,scc_cnt,scc[maxn];
int instc[maxn],stc[maxn],top,ans1,ans2;
int ind[maxn],oud[maxn];
char chr[1010][1010];
void update()
{
cnt=times=scc_cnt=top=ans1=ans2=0;
memset(Laxt,0,sizeof(Laxt));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(scc,0,sizeof(scc));
memset(instc,0,sizeof(instc));
memset(stc,0,sizeof(stc));
memset(ind,0,sizeof(ind));
memset(oud,0,sizeof(oud));
}
void add(int u,int v)
{
Next[++cnt]=Laxt[u];
Laxt[u]=cnt;
To[cnt]=v;
}
void dfs(int u)
{
dfn[u]=low[u]=++times;
stc[++top]=u; instc[u]=1;
for(int i=Laxt[u];i;i=Next[i]){
int v=To[i];
if(!dfn[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(instc[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
scc_cnt++;
while(true){
int x=stc[top--];
scc[x]=scc_cnt;
instc[x]=0;
if(x==u) break;
}
}
}
void tarjan()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(chr[i][j]=='.'&&!dfn[(i-1)*m+j]) dfs((i-1)*m+j);
}
for(int i=1;i<=n*m;i++)
for(int j=Laxt[i];j;j=Next[j]){
if(scc[i]!=scc[To[j]]) {
ind[scc[To[j]]]++;
oud[scc[i]]++;
}
}
for(int i=1;i<=scc_cnt;i++){
if(ind[i]==0) ans1++;
if(oud[i]==0) ans2++;
}
}
int main()
{
while(~scanf("%d%d",&n,&m)){
update();
for(int i=1;i<=n;i++) scanf("%s",chr[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(chr[i][j]=='.'){
if(j+1<=m&&chr[i][j+1]=='.') add((i-1)*m+j,(i-1)*m+j+1);
if(i+1<=n&&chr[i+1][j]=='.') add((i-1)*m+j,i*m+j);
}
}
tarjan();
if(scc_cnt==1) printf("0\n");
else printf("%d\n",max(ans1,ans2));
} return 0;
}
利用方向只能像东南这个条件,直接检查是是不是入度为0的点,或者出度为0的点。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=0,ff=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();}
return xx*ff;
}
const int ws_[4]={0,1,0,-1},ad_[4]={1,0,-1,0};
int N,M;
char mp[1010][1010];
bool vis[1010][1010];
int tp1,tp2,cnt;
bool check(int xx,int yy){
if(xx<=0||yy<=0||xx>N||yy>M)
return 0;
return 1;
}
void dfs(int xx,int yy){
vis[xx][yy]=1;
if((!check(xx+ws_[2],yy+ad_[2]))||mp[xx+ws_[2]][yy+ad_[2]]=='#')
if((!check(xx+ws_[3],yy+ad_[3]))||mp[xx+ws_[3]][yy+ad_[3]]=='#')
tp1++;
if((!check(xx+ws_[0],yy+ad_[0]))||mp[xx+ws_[0]][yy+ad_[0]]=='#')
if((!check(xx+ws_[1],yy+ad_[1]))||mp[xx+ws_[1]][yy+ad_[1]]=='#')
tp2++;
int tx,ty;
for(int k=0;k<=3;k++){
tx=xx+ws_[k],ty=yy+ad_[k];
if(!check(tx,ty))
continue;
if((!vis[tx][ty])&&mp[tx][ty]=='.')
dfs(tx,ty);
}
}
int main(){
//freopen("in","r",stdin);
while(scanf("%d %d",&N,&M)!=EOF){
cnt=0;
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++){
char ch=getchar();
while(ch==' '||ch=='\n')
ch=getchar();
mp[i][j]=ch;
if(mp[i][j]=='.')
cnt++;
}
int ans,ans1=0,ans2=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
if(mp[i][j]=='.'&&(!vis[i][j])){
tp1=tp2=0;
dfs(i,j);
ans1+=tp1,ans2+=tp2;
}
ans=max(ans1,ans2);
if(cnt==1)
ans=0;
printf("%d\n",ans);
}
return 0;
}
【E】
显然,缩点,重新建图,只有入度为0的点(缩点之后的)是需要考虑的,如果入度为0的点(缩点之后,其实是个集合)里面如果没有可以通知到的,那么“-1”;
如果入度为0的点大于m,那么“-1”;
现在看来,只需求入度为0的有哪些,然后验证每个点(集合)是否存在可以通知的人即可,而不需重新构图。


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1010;
int Laxt[maxn],Next[maxn*250],To[maxn*250],cnt,n,m;
int dfn[maxn],low[maxn],times,scc_cnt,scc[maxn];
int instc[maxn],stc[maxn],top;
int ind[maxn],ans,p[maxn],ok[maxn];
void update()
{
cnt=times=scc_cnt=top=ans=0;
memset(Laxt,0,sizeof(Laxt));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(instc,0,sizeof(instc));
memset(stc,0,sizeof(stc));
memset(ind,0,sizeof(ind));
memset(p,0,sizeof(p));
memset(ok,0,sizeof(ok));
}
void add(int u,int v)
{
Next[++cnt]=Laxt[u];
Laxt[u]=cnt;
To[cnt]=v;
}
void dfs(int u)
{
dfn[u]=low[u]=++times;
stc[++top]=u; instc[u]=1;
for(int i=Laxt[u];i;i=Next[i]){
int v=To[i];
if(!dfn[v]){
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(instc[v]){
low[u]=min(low[u],dfn[v]);
}
}
if(dfn[u]==low[u]){
scc_cnt++;
while(true){
int x=stc[top--];
scc[x]=scc_cnt;
instc[x]=0;
if(x==u) break;
}
}
}
void tarjan()
{
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i);
for(int i=1;i<=n;i++)
for(int j=Laxt[i];j;j=Next[j]){
if(scc[i]!=scc[To[j]]) {
ind[scc[To[j]]]++;
}
}
for(int i=1;i<=scc_cnt;i++){
if(ind[i]==0) ans++;
}
if(ans>m){
printf("-1\n");
return ;
}
for(int i=1;i<=n;i++){
if(ind[scc[i]]==0&&p[i]==1) ok[scc[i]]=1;
}
for(int i=1;i<=scc_cnt;i++)
if(ind[i]==0&&!ok[i]) {
printf("-1\n");
return ;
}
printf("%d\n",ans);
return ;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
update(); int x,y;
for(int i=1;i<=m;i++) scanf("%d",&x),p[x]=1;
for(int i=1;i<=n;i++) {
scanf("%d",&y);
for(int j=1;j<=y;j++){
scanf("%d",&x);
add(i,x);
}
}
tarjan();
} return 0;
}
【F】
水题。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=0,ff=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();}
return xx*ff;
}
const int maxn=60,maxm=2010;
int N,M;
int lin[maxn],len;
struct edge{
int y,next;
}e[maxm<<1];
inline void insert(int xx,int yy){
e[++len].next=lin[xx];
lin[xx]=len;
e[len].y=yy;
}
bool vis[maxn];
void dfs(int x){
vis[x]=1;
for(int i=lin[x];i;i=e[i].next)
if(!vis[e[i].y])
dfs(e[i].y);
}
int main(){
//freopen("in","r",stdin);
while(scanf("%d %d",&N,&M)!=EOF){
int t1,t2;
memset(lin,0,sizeof(lin));len=0;
for(int i=1;i<=M;i++){
t1=read(),t2=read();
insert(t1,t2);
}
memset(vis,0,sizeof(vis));
dfs(1);
if(vis[N])
cout<<"Yes\n";
else
cout<<"No\n";
}
return 0;
}
【G】
大家应该会感觉是最短路算法,或者搜索?因为记忆化后节点数也不多。
0代表什么都不拿....7代表1+1+1,拿了三样,这样就是分层处理。
然后每次4个方向。。。我tm居然列举出来,而不是for语句合并。。。。导致代码太丑了。。。
这里用的分层+SPFA,然后用BFS的方法(优先单调队列+STL结构体 实现)搜索的。
由于后面复制的时候排版乱了,将就吧。。。


#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
char chr[110][110];
int dis[8][110][110],times[10],n,m;
int sx,sy,xx[4],yy[4],ex,ey,t1,t2,t3;
const int inf=0x7fffffff;
struct In
{
int opt1;
int x1;
int y1;
int stp1;
In(int i, int j,int k,int t):opt1(i), x1(j), y1(k), stp1(t){}
};
bool operator < (const In& x, const In& y)
{
return x.stp1 > y.stp1;
}
priority_queue<In>q;
void dfs()
{
while(!q.empty())
{
In tmp=q.top();q.pop();
int opt=tmp.opt1,x=tmp.x1,y=tmp.y1,stp=tmp.stp1;
if(opt==7&&x==ex&&y==ey) return ;
if(opt<7){//可以翻墙
if(x+1<=n) {
In temp(opt,x+1,y,stp+times[opt]);
if(dis[opt][x+1][y]>stp+times[opt]) dis[opt][x+1][y]=stp+times[opt],q.push(temp);
for(int i=1;i<=3;i++){
if(x==xx[i]&&y==yy[i]&&!((opt>>(i-1))&1)) {
In temp(opt+(1<<(i-1)),x+1,y,stp+times[opt+(1<<(i-1))]);
if(dis[opt+(1<<(i-1))][x+1][y]>stp+times[opt+(1<<(i-1))]) dis[opt+(1<<(i-1))][x+1][y]=stp+times[opt+(1<<(i-1))],q.push(temp);
}
}
}
if(x-1>=1){
In temp(opt,x-1,y,stp+times[opt]);
if(dis[opt][x-1][y]>stp+times[opt]) dis[opt][x-1][y]=stp+times[opt],q.push(temp);
for(int i=1;i<=3;i++){
if(x==xx[i]&&y==yy[i]&&!((opt>>(i-1))&1)) {
In temp(opt+(1<<(i-1)),x-1,y,stp+times[opt+(1<<(i-1))]);
if(dis[opt+(1<<(i-1))][x-1][y]>stp+times[opt+(1<<(i-1))]) dis[opt+(1<<(i-1))][x-1][y]=stp+times[opt+(1<<(i-1))],q.push(temp);
}
}
}
if(y-1>=1){
In temp(opt,x,y-1,stp+times[opt]);
if(dis[opt][x][y-1]>stp+times[opt]) dis[opt][x][y-1]=stp+times[opt],q.push(temp);
for(int i=1;i<=3;i++){
if(x==xx[i]&&y==yy[i]&&!((opt>>(i-1))&1)) {
In temp(opt+(1<<(i-1)),x,y-1,stp+times[opt+(1<<(i-1))]);
if(dis[opt+(1<<(i-1))][x][y-1]>stp+times[opt+(1<<(i-1))]) dis[opt+(1<<(i-1))][x][y-1]=stp+times[opt+(1<<(i-1))],q.push(temp);
}
}
}
if(y+1<=m){
In temp(opt,x,y+1,stp+times[opt]);
if(dis[opt][x][y+1]>stp+times[opt]) dis[opt][x][y+1]=stp+times[opt],q.push(temp);
for(int i=1;i<=3;i++){
if(x==xx[i]&&y==yy[i]&&!((opt>>(i-1))&1)) {
In temp(opt+(1<<(i-1)),x,y+1,stp+times[opt+(1<<(i-1))]);
if(dis[opt+(1<<(i-1))][x][y+1]>stp+times[opt+(1<<(i-1))]) dis[opt+(1<<(i-1))][x][y+1]=stp+times[opt+(1<<(i-1))],q.push(temp);
}
}
}
}
else {//走路
if(x+1<=n&&chr[x+1][y]=='.') {
In temp(opt,x+1,y,stp+times[opt]);
if(dis[opt][x+1][y]>stp+times[opt]) dis[opt][x+1][y]=stp+times[opt],q.push(temp);
}
if(x-1>=1&&chr[x-1][y]=='.'){
In temp(opt,x-1,y,stp+times[opt]);
if(dis[opt][x-1][y]>stp+times[opt]) dis[opt][x-1][y]=stp+times[opt],q.push(temp);
}
if(y-1>=1&&chr[x][y-1]=='.'){
In temp(opt,x,y-1,stp+times[opt]);
if(dis[opt][x][y-1]>stp+times[opt]) dis[opt][x][y-1]=stp+times[opt],q.push(temp);
}
if(y+1<=m&&chr[x][y+1]=='.'){
In temp(opt,x,y+1,stp+times[opt]);
if(dis[opt][x][y+1]>stp+times[opt]) dis[opt][x][y+1]=stp+times[opt],q.push(temp);
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%s",chr[i]+1);
for(int i=0;i<=7;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=m;k++)
dis[i][j][k]=inf;
scanf("%d%d%d%d%d%d%d%d%d%d",&sx,&sy,&xx[1],&yy[1],&xx[2],&yy[2],&xx[3],&yy[3],&ex,&ey);
scanf("%d%d%d",&t1,&t2,&t3);
times[0]=1; times[1]=1+t1;times[2]=1+t2;times[3]=1+t1+t2;
times[4]=1+t3;times[5]=1+t1+t3;times[6]=1+t2+t3;times[7]=1+t1+t2+t3;
In tmp(0,sx,sy,0);
q.push(tmp);
dfs();
printf("%d\n",dis[7][ex][ey]);
return 0;
}
【H】
STL里面有个permutation,不过要自己手动写展开也不难。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
using namespace std;
int read(){
int xx=0,ff=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();}
return xx*ff;
}
int main(){
int num[8]={1,2,3,4,5,6,7,8};
do{
for(int i=0;i<7;i++)
printf("%d ",num[i]);
printf("%d",num[7]);
puts("");
}while(next_permutation(num,num+8));
return 0;
}
来源:oschina
链接:https://my.oschina.net/u/4317554/blog/4234863