2016长乐夏令营 Day12

41 篇文章 0 订阅
14 篇文章 0 订阅

T1:

f[i][j]:吃掉的黑子状态为i,当前在第j个棋子的位置上,走的最小步数

因为棋盘是不断更新的,所以每个状态要更新后续状态的时候要一遍BFS

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;

const int maxn = 20;
const int maxm = 1<<15;
const int INF = ~0U>>1;
const int dx[8] = {0,1,0,-1,1,1,-1,-1};
const int dy[8] = {1,0,-1,0,1,-1,1,-1};
const int kx[8] = {2,2,1,1,-1,-1,-2,-2};
const int ky[8] = {1,-1,2,-2,2,-2,1,-1};

struct P{
	int A,B;
	P(int _A = 0,int _B = 0) {A = _A; B = _B;}	
};

struct Point{
	int x,y;
	Point(int _x = 0,int _y = 0) {x = _x; y = _y;}
};

int n,m,tot,pox[maxn],poy[maxn],f[maxm][16],Mark[maxn][maxn],num[maxn][maxn]
	,cnt,vis[maxn][maxn],dis[maxn][maxn],B[maxn],typ[maxn];
char p[maxn][maxn];
bool inq[maxm][16]; 

queue <P> Q1;
queue <Point> Q2;

void Work(int tx,int ty,int l)
{
	for (;;) {
		tx += dx[l]; ty += dy[l];
		if (tx < 0 || tx == n || ty < 0 || ty == m) return;
		Mark[tx][ty] = cnt;
		if (B[num[tx][ty]] != cnt) return;
	}
}

void Mark1(int qx,int qy)
{
	for (int i = 0; i < 8; i++) {
		int xx = qx + kx[i];
		int yy = qy + ky[i];
		if (xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
		Mark[xx][yy] = cnt;
	}
}

void Mark2(int qx,int qy)
{
	for (int i = 4; i < 8; i++)
		Work(qx,qy,i);
}

void Mark3(int qx,int qy)
{
	for (int i = 0; i < 4; i++)
		Work(qx,qy,i);
}

void BFS(int now,int px,int py)
{
	int nx = now; B[0] = cnt;
	for (int i = 1; i <= tot; i++) {
		if (nx&1) B[i] = cnt;
		nx >>= 1;
	} 
	nx = now;
	for (int i = 1; i <= tot; i++) {
		if (!(nx&1)) {
			if (typ[i] == 1) Mark1(pox[i],poy[i]);
			if (typ[i] == 2) Mark2(pox[i],poy[i]);
			if (typ[i] == 3) Mark3(pox[i],poy[i]);
		}
		nx >>= 1; 
	}
	vis[px][py] = cnt; dis[px][py] = 0;
	Q2.push(Point(px,py));
	while (!Q2.empty()) {
		Point k = Q2.front(); Q2.pop();
		for (int i = 0; i < 8; i++) {
			int xx = k.x + dx[i];
			int yy = k.y + dy[i];
			if (xx < 0 || xx == n || yy < 0 || yy == m) continue;
			if (vis[xx][yy] == cnt || Mark[xx][yy] == cnt) continue;
			vis[xx][yy] = cnt; dis[xx][yy] = dis[k.x][k.y] + 1;
			Q2.push(Point(xx,yy));
		}
	}
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("chess.in","r",stdin);
		freopen("chess.out","w",stdout);
	#endif
	
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		scanf("%s",p[i]);
		for (int j = 0; j < m; j++)
			if (p[i][j] != '.') {
				if (p[i][j] == '*') {
					pox[0] = i; poy[0] = j;
					continue;
				}
				++tot; pox[tot] = i; poy[tot] = j;
				num[i][j] = tot;
				if (p[i][j] == 'K') typ[tot] = 1;
				if (p[i][j] == 'B') typ[tot] = 2;
				if (p[i][j] == 'R') typ[tot] = 3;
			}
	}
	if (!tot) {cout << 0; return 0;}
	
	for (int i = 0; i < (1<<tot); i++)
		for (int j = 0; j <= tot; j++)
			f[i][j] = INF;
	Q1.push(P(0,0)); 
	f[0][0] = 0; inq[0][0] = 1;	
	while (!Q1.empty()) {
		P k = Q1.front(); Q1.pop();
		++cnt; BFS(k.A,pox[k.B],poy[k.B]);
		int now = k.A;
		for (int pos = 1; pos <= tot; pos++,now >>= 1) {
			if (!(now&1)) {
				int Next = k.A|(1<<(pos-1));
				if (vis[pox[pos]][poy[pos]] != cnt) continue;
				if (f[Next][pos] > f[k.A][k.B] + dis[pox[pos]][poy[pos]]) {
					f[Next][pos] = f[k.A][k.B] + dis[pox[pos]][poy[pos]];
					if (!inq[Next][pos]) Q1.push(P(Next,pos)),inq[Next][pos] = 1;
				}
			}	
		}
	}
	
	int ans = INF;
	for (int i = 0; i <= tot; i++)
		ans = min(ans,f[(1<<tot)-1][i]);
	if (ans == INF) cout << -1;
	else cout << ans; 
	return 0;
}


T2:

首先,,是棵斯坦纳树不会错

但是,题目只是要求每户人家能进一个屋子

所以,我们只要求出一些连通块使得能配出k对就好

f[i][j]:关键点连通情况为i,当前根为j的斯坦纳树

g[i]:关键点配对情况为i的最优方案

对于g,g的初值不能由f直接转移,要f中状态恰好两两配对的才能赋为初值

斯坦纳树+dp,方程不再赘述

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;

const int maxn = 55;
const int maxm = 1<<11;
const int INF = 1E9;

struct E{
	int to,w;
	E(int _to = 0,int _w = 0) {to = _to; w = _w;}
};

int n,m,k,T,f[maxm][maxn],g[maxm],vis[maxn];

vector <E> v[maxn];
queue <int> Q;

void SPFA(int o)
{
	while (!Q.empty()) {
		int K = Q.front(); Q.pop(); vis[K] = 0;
		for (int i = 0; i < v[K].size(); i++) {
			int to = v[K][i].to;
			if (f[o][to] > f[o][K] + v[K][i].w) {
				f[o][to] = f[o][K] + v[K][i].w;
				if (!vis[to]) vis[to] = 1,Q.push(to);
			}
		}
	}
}

bool check(int o)
{
	int K = k/2,tot = 0;
	for (int i = 0; i < K; i++) {
		if (o&1) ++tot;
		o >>= 1;
	}
	for (int i = 0; i < K; i++) {
		if (o&1) --tot;
		o >>= 1;
	}
	return !tot;
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("spring.in","r",stdin);
		freopen("spring.out","w",stdout);
	#endif
	
	cin >> T;
	while (T--) {
		scanf("%d%d%d",&n,&m,&k);
		while (m--) {
			int x,y,z; scanf("%d%d%d",&x,&y,&z);
			v[x].push_back(E(y,z));
			v[y].push_back(E(x,z));
		}
		
		int pos = n - k + 1; k <<= 1;
		for (int i = 0; i < (1<<k); i++)
			for (int j = 1; j <= n; j++)
				f[i][j] = INF;
		for (int i = 1; i <= k/2; i++) f[1<<(i-1)][i] = 0;
		for (int i = k/2 + 1; i <= k; i++) f[1<<(i-1)][pos] = 0,++pos;
		for (int o = 0; o < (1<<k); o++) {
			for (int i = 1; i <= n; i++) {
				for (int op = (o - 1) & o; op; op = (op - 1) & o) 
					f[o][i] = min(f[o][i],f[op][i] + f[o - op][i]);
				if (f[o][i] != INF) Q.push(i),vis[i] = 1;
			}
			SPFA(o);
		}
		for (int i = 0; i < (1<<k); i++) {
			g[i] = INF;
			if (check(i)) 
				for (int j = 1; j <= n; j++)
					g[i] = min(g[i],f[i][j]);
		}
		for (int o = 0; o < (1<<k); o++)
			for (int op = (o - 1) & o; op; op = (op - 1) & o)
				g[o] = min(g[o],g[op] + g[o - op]);
		if (g[(1<<k) - 1] != INF) printf("%d\n",g[(1<<k) - 1]);
		else printf("No solution\n");
 		for (int i = 1; i <= n; i++) v[i].clear();
	}
	return 0;
}

T3:

行和列的变换不受顺序影响,单个变换可以留到最后

行和列的变换方式为4!*4!种,枚举出来,快速判断单个的变换就好

写的时候居然dfs行列变换,,,我简直傻逼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<bitset>
using namespace std;

int ans = 16,cnt,a[10][10],bx[10],by[10],nx[10],ny[10],
	tx[10],ty[10],b[10][10],bo[20],sx[20],sy[20];

int QuickJudge()
{
	int ret,sum; 
	++cnt; ret = 0;
	for (int i = 1; i <= 4; i++)
		for (int j = 1; j <= 4; j++)
			if (sx[b[i][j]] != i || sy[b[i][j]] != j) {
				if (bo[b[i][j]] == cnt) continue;
				int now = b[i][j]; sum = 0;
				for (;;) {
					++sum; bo[now] = cnt;
					int xx = sx[now];
					int yy = sy[now];
					now = b[xx][yy];
					if (now == b[i][j]) break;
				}
				ret += (sum - 1);
			}
	return ret;
}

int getint()
{
	int ret = 0;
	char ch = getchar();
	while (ch < '0' || '9' < ch) ch = getint();
	while ('0' <= ch && ch <= '9')
		ret = ret*10 + ch - '0',ch = getchar();
	return ret;
}

void Judge()
{
	int tot = 0; ++cnt;
	for (int i = 1; i <= 4; i++) {
		int sum = 0;
		if (tx[nx[i]] != cnt) {
			int now = nx[i];
			for (;;) {
				++sum; tx[now] = cnt;
				now = nx[now];
				if (now == nx[i]) break;
			}
			tot += (sum-1);
		} 
		sum = 0;
		if (ty[ny[i]] != cnt) {
			int now = ny[i];
			for (;;) {
				++sum; ty[now] = cnt;
				now = ny[now];
				if (now == ny[i]) break;
			}
			tot += (sum-1);
		}
	}
	for (int i = 1; i <= 4; i++)
		for (int j = 1; j <= 4; j++)
			b[i][j] = a[nx[i]][ny[j]];
	tot += QuickJudge();
	ans = min(ans,tot);
}

void dfs2(int y)
{
	if (y == 5) {Judge(); return;}
	for (int i = 1; i <= 4; i++) 
		if (!by[i]) {
			by[i] = 1; ny[y] = i;
			dfs2(y+1);
			by[i] = 0;
		}
}

void dfs1(int x)
{
	if (x == 5) {dfs2(1); return;}
	for (int i = 1; i <= 4; i++) 
		if (!bx[i]) {
			bx[i] = 1; nx[x] = i;
			dfs1(x+1);
			bx[i] = 0;
		}
}

int main()
{
	#ifdef DMC
		freopen("DMC.txt","r",stdin);
    #else
		freopen("puzzle.in","r",stdin);
		freopen("puzzle.out","w",stdout);
	#endif
	
	for (int i = 1; i <= 4; i++)
		for (int j = 1; j <= 4; j++)
			a[i][j] = getint();
	int X = 1,Y = 1;
	for (int i = 1; i <= 16; i++) {
		sx[i] = X; sy[i] = Y;
		++Y;
		if (Y > 4) ++X,Y = 1;
	}
	
	dfs1(1);
	cout << ans; 
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值