您的位置:首页 > 其它

2018年全国多校算法寒假训练营练习比赛(第四场)

2018-02-11 22:13 651 查看
地址:点击打开链接
A 石油采集
思路:
二维矩阵四连通图是一个二分图,其实仔细想想就是将相邻的“#”建图,然后在找最大匹配。匈牙利算法即可。也可以dfs,代码如下:
匈牙利:#include<iostream>
#include<stdlib.h>
#include<cstdio>
#include<cstring>
#include<string>
#include<time.h>
#include<algorithm>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<deque>
#include<map>
#include<set>
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
typedef long long ll;
using namespace std;
int dir[4][2]={0,1,1,0,0,-1,-1,0};
int n,line[2555][2555],M[105][105],pre[10005],vis[10005];
char a[105][105];

int Find(int x){
for(int i=1;i<=n*n;i++){
if(!vis[i]&&line[x][i]){
vis[i]=1;
if(pre[i]==-1||Find(pre[i])){
pre[i]=x;
return 1;
}
}
}
return 0;
}

int main()
{
int T,count=1;
scanf("%d",&T);
while(T--){
memset(line,0,sizeof(line));
memset(pre,-1,sizeof(pre));
scanf("%d",&n);
getchar();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%c",&a[i][j]);
}
getchar();
}
int k=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
M[i][j]=k++;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(a[i][j]=='#'&&(i+j-1)%2){ //奇偶区分
for(int l=0;l<4;l++){
int xx=i+dir[l][0];
int yy=j+dir[l][1];
if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&a[xx][yy]=='#'){
line[M[i][j]][M[xx][yy]]=1;
}
}
}
}
}
int ans=0;
for(int i=1;i<=n*n;i++){
memset(vis,0,sizeof(vis));
if(Find(i))
ans++;
}
printf("Case %d: %d\n",count,ans);
count++;
}
return 0;
}DFS代码:#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>

using namespace std;

const int maxn = 55;
const int INF = 0x3f3f3f3f;
int N;
char MAP[maxn][maxn];
int a[4][2] = {1,0,0,1,-1,0,0,-1};
int number = 0;
bool check(int x,int y)
{
return (x >=0 && y >= 0 && y < N && x < N);
}
bool dfs(int x,int y)
{
for(int i = 0 ; i <= 3; i ++)
{
int hx = x + a[i][0];
int yx = y + a[i][1];
if(check(hx,yx) && MAP[hx][yx] == '#')
{
MAP[x][y] = '.';
if(dfs(hx,yx) == false)
{
number++;
MAP[hx][yx] = '.';
MAP[x][y] = '.';
return true;

}
MAP[x][y] = '#';//回溯
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
int CASE = 1;
while(T--)
{
scanf("%d",&N);
for(int i = 0; i < N ; i ++)
{
scanf("%s",&MAP[i]);
}
for(int i = 0; i < N ; i ++)
{
for(int j = 0; j < N ; j ++)
{
if(MAP[i][j] == '#')
{
dfs(i,j);
}
}
}
printf("Case %d: %d\n",CASE++,number);
}
return 0;
}B 道路建设
思路:水题。裸的最小生成树。kruskal模板即可:#include<map>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 10000+100
#define PI acos(-1.0)
#define INF 1e9
using namespace std;
typedef long long ll;
int c,n,m;
int p[maxn],w[maxn],u[maxn],v[maxn],r[maxn];

int cmp(const int i,const int j){
return w[i]<w[j];
}

int findset(int x){
return p[x]==x?x:p[x]=findset(p[x]);
}

int kruskal(){
int ans=0;
for(int i=0;i<n;i++)p[i]=i;
for(int i=0;i<m;i++)r[i]=i;
sort(r,r+m,cmp);
for(int i=0;i<m;i++){
int e=r[i];
int x=findset(u[e]);
int y=findset(v[e]);
if(x!=y){
ans+=w[e];
p[x]=y;
}
}
return ans;
}

int main(){

cin>>c>>m>>n;
for(int i=0;i<m;i++){
cin>>u[i]>>v[i]>>w[i];
}
int ans=kruskal();
if(ans>c) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
return 0;
}

C 求交集

思路:
水题啊!想太复杂了,因为数据是上升序的,所以只需要暴力判断即可,代码如下:#include <cstdio>
#include <cstring>
const int MAXN=1e6+10;
int n,m;
int a[MAXN],b[MAXN];
bool flag;

void print(int x)
{
if (flag)
printf("%d",x);
else
printf(" %d",x);
flag=false;
}

int main()
{
flag=true;
while (scanf("%d%d",&n,&m)==2)
{
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=m;i++)
scanf("%d",&b[i]);
int i=1,j=1;
while (i<=n && j<=m)
{
if (a[i]==b[j])
print(a[i]),i++,j++;
else
if (a[i]<b[j])
i++;
else
j++;
}
if (flag)
printf("empty");
printf("\n");
flag=true;
}
return 0;
}D 小明的挖矿之旅
思路:由于只能向右或者向下走,所以变成了一张有向无环图。我们只要比较出度为0的点的个数和入度为0的点的个数即可。
注意几种特殊情况:全是孤立点、没有点代码:(参考:点击打开链接)#include <bits/stdc++.h>
using namespace std;

const int INF = 0x7FFFFFFF;
const int maxn = 1100;
char s[maxn][maxn];
int in[maxn * maxn];
int ou[maxn * maxn];
int n, m;

int out(int x, int y) {
if(x < 0 || x >= n) return 1;
if(y < 0 || y >= m) return 1;
return 0;
}

int main() {
while(~scanf("%d%d", &n, &m)) {
for(int i = 0; i < n; i ++) {
scanf("%s", s[i]);
}
memset(in, 0, sizeof in);
memset(ou, 0, sizeof ou);
for(int i = 0; i < n; i ++) {
for(int j = 0; j < m; j ++) {
if(s[i][j] == '#') continue;
if(out(i, j + 1) == 0 && s[i][j + 1] != '#') {
in[i * m + j + 1] ++;
ou[i * m + j] ++;
}
if(out(i + 1, j) == 0 && s[i + 1][j] != '#') {
in[(i + 1) * m + j] ++;
ou[i * m + j] ++;
}
}
}

int sum1 = 0, sum2 = 0, sum3 = 0;
for(int i = 0; i < n; i ++) {
for(int j = 0; j < m; j ++) {
if(s[i][j] == '#') continue;
if(in[i * m + j] == 0) sum1 ++;
if(ou[i * m + j] == 0) sum2 ++;
sum3 ++;
}
}

if(sum3 == 0) {
printf("%d\n", 0);
return 0;
}
if(sum1 == sum3 && sum2 == sum3) {
printf("%d\n", sum1 - 1);
}
else
printf("%d\n", max(sum1, sum2));
}

return 0;
}E 通知小弟

思路: 先对图进行强连通分量缩点,因为一个强连通分量内部一旦有一个人得到通知,所有人都可以得到通知。缩点后形成了一张有向无环图,我们只要通知到新图中那些入度为0的点,所有的人都能得到通知。对于无解的情况,我们只要检查HA能通知到的人是否cover了新图中所有入度为0的点。
代码:

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<iomanip>
#include<set>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=505 ;
const int M=505*505 ;
const int inf=1<<20 ;
struct node
{
int u,v,next ;
}edge[M] ;
int head
,low
,dnf
,vis
,stack
,in
,belong
,cost
,f[505];
int dep,cnt,top,sum ;

void add(int u ,int v)
{
edge[top].u=u ;
edge[top].v=v;
edge[top].next=head[u] ;
head[u]=top++;
}

void tarjan(int u)
{
int x ;
low[u]=dnf[u]=++dep ;
stack[cnt++]=u;
vis[u]=1;
for(int i = head[u] ; i!=-1; i=edge[i].next)
{
int v=edge[i].v ;
if(!dnf[v])
{
tarjan(v) ;
low[u] = min( low[u] , low[v]) ;
}
else if(vis[v])
low[u] = min( low[u] , dnf[v] ) ;
}
if(low[u]==dnf[u])
{
sum++ ;
int z=inf ;
do
{
x = stack[--cnt] ;
vis[x] = 0 ;
belong[x]=sum;
z = min(z,f[x]) ;
}while(x!=u) ;
cost[sum] = z ;//更新一个联通分支花费最小的 ;
}
}

int main()
{
int n,m ,u,v,x;
while(~scanf("%d%d",&n,&m))
{
top = 0 ;
memset(head,-1,sizeof(head));

for(int i = 1 ; i <= m;i++)
scanf("%d", &f[i]);

for(int i = 1 ; i <= n ; i++)
{
scanf("%d", &x);
for (int j = 0; j < x; j++) {
scanf("%d",&u) ;
add(i,u) ;
}

}

memset(low,0,sizeof(low)) ;
memset(dnf,0,sizeof(dnf)) ;
memset(vis,0,sizeof(vis)) ;
memset(in,0,sizeof(in)) ;
dep=sum=cnt=0 ;
for(int i = 1 ; i <= n ; i++)
{
if(!dnf[i])
tarjan(i) ;
}

for(int i = 1 ; i <= n ; i++)//缩点
{
for(int j = head[i] ; j!=-1 ;j=edge[j].next)
{
v=edge[j].v ;
if(belong[i]!= belong[v])//不同强连通分支
{
in[belong[v]]++ ;//记录入读
}
}
}

int ans = 0 , flag= 1;
bool ok;
for(int i = 1 ; i <= sum ; i++)
if(!in[i]) {
ok = false;
for (int j = 1; j <= m; j++)
if (belong[f[j]] == i) {
ok = true;
break;
}
if (!ok) {
flag = 0;
break;
}
ans++;
}

if (flag) printf("%d\n", ans);
else puts("-1");

}
return 0 ;
}F Call to your teacher
思路:
bfs暴力即可
代码:

#include<map>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 2000+100
#define PI acos(-1.0)
#define INF 1e9
using namespace std;
typedef long long ll;
int n,m;
int vis[maxn];
vector<int>G[maxn];

bool bfs(){

queue<int>Q;
Q.push(1);
vis[1]=true;
while(!Q.empty()){
int x=Q.front();
Q.pop();
vis[x]=true;
// if(x==n) return true;
for(int i=0;i<(int)G[x].size();i++){
if(G[x][i]==n) return true;
if(!vis[G[x][i]]) Q.push(G[x][i]);
else continue;
}
}
return false;
}

int main(){

cin>>n>>m;
int x,y;
memset(vis,0,sizeof(vis));
for(int i=0;i<=n;i++) G[i].clear();
for(int i=0;i<m;i++){
cin>>x>>y;
G[x].push_back(y);
}
if(bfs()){
printf("Yes\n");

}
else {
printf("No\n");
}
return 0;
}
G 老子的意大利炮呢
思路:可以枚举三种配件按什么顺序获得,得到之后再走到终点即可。最后阶段可以bfs。
代码:#include <bits/stdc++.h>
using namespace std;

const int INF = 0x7FFFFFFF;
const int maxn = 110;
char s[maxn][maxn];
int dis[maxn][maxn];
int ans;
int n, m;
int sx, sy;
int x[5], y[5];
int ex, ey;
int t[5];
int dir[4][2] = {{-1, 0},{1, 0},{0, -1},{0, 1},};
int out(int x, int y) {
if(x < 0 || x >= n) return 1;
if(y < 0 || y >= m) return 1;
return 0;
}
void bfs() {
queue<int> q;
q.push(ex * m + ey);
dis[ex][ey] = 0;
while(!q.empty()) {
int top = q.front();
q.pop();
int nowx = top / m;
int nowy = top % m;
for(int i = 0; i < 4; i ++) {
int tx = nowx + dir[i][0];
int ty = nowy + dir[i][1];
if(out(tx, ty)) continue;
if(s[tx][ty] == '#') continue;
if(dis[nowx][nowy] + t[0] + t[1] + t[2] + 1 > dis[tx][ty]) continue;
dis[tx][ty] = dis[nowx][nowy] + t[0] + t[1] + t[2] + 1;
q.push(tx * m + ty);
}
}
}

int D(int x1, int y1, int x2, int y2) {
return abs(x1 - x2) + abs(y1 - y2);
}

int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i ++) {
scanf("%s", s[i]);
for(int j = 0; j < m; j ++) {
dis[i][j] = INF;
}
}
scanf("%d%d", &sx, &sy);
sx --; sy --;
for(int i = 0; i < 3; i ++) {
scanf("%d%d", &x[i], &y[i]);
x[i] --;
y[i] --;
}
scanf("%d%d", &ex, &ey);
ex --; ey --;
for(int i = 0; i < 3; i ++) {
scanf("%d", &t[i]);
}
bfs();
ans = INF;
for(int i = 0; i < 3; i ++) {
if(dis[x[i]][y[i]] == INF) continue;
int tmp = 0;
int p0, p1, p2;
if(i == 0) {
p0 = 0;
p1 = 1;
p2 = 2;
} else if(i == 1) {
p0 = 1;
p1 = 0;
p2 = 2;
} else {
p0 = 2;
p1 = 1;
p2 = 0;
}

tmp = min(D(sx, sy, x[p1], y[p1]) * 1
+ D(x[p1], y[p1], x[p2], y[p2]) * (t[p1] + 1)
+ D(x[p2], y[p2], x[p0], y[p0]) * (t[p1] + t[p2] + 1),
D(sx, sy, x[p2], y[p2]) * 1
+ D(x[p1], y[p1], x[p2], y[p2]) * (t[p2] + 1)
+ D(x[p1], y[p1], x[p0], y[p0]) * (t[p1] + t[p2] + 1)
);

tmp += dis[x[i]][y[i]];
ans = min(ans, tmp);
}
cout << ans << endl;
return 0;
}H 老子的全排列呢
思路:
dfs求全排列即可
代码:#include<map>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define maxn 1000+100
#define maxm 200000+100
#define PI acos(-1.0)
#define INF 1e9
using namespace std;
typedef long long ll;
int m=8;
int vis[1000000];
int a[1000000];
void dfs(int n)
{
if(n==m)
{
for(int j=1;j<=m-1;j++)
printf("%d ",a[j]);
printf("%d",a[m]);
printf("\n");
}
n++;
for(int i=1;i<=m;i++)
{
if(vis[i]) continue;
vis[i]=1;
a
=i;
dfs(n);
vis[i]=0;
}
}
int main()
{
memset(vis,0,sizeof(vis));
dfs(0);
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  图论
相关文章推荐