soj 2013 weekly-2 7574-7675 7691-7593

7674

题意:n个人(0~n-1)坐成一圈,有p个石头,从第0个人开始,如果还有石头,那么他从中取出一个,如果没有石头,那么他把自己所有的石头贡献出来,接着轮到下一个,问最后p个石头会到谁那去。

思路:简单模拟即可。

#include <cstdio>
#include <cstring>
int n, p, arr[55], curP, curN;
int main()
{
	while (true)
	{
		scanf("%d%d",&n,&p);
		if (n == 0 && p == 0) break;
		memset(arr, 0, sizeof(arr));
		curN = p;
		curP = 0;
		while (true)
		{
			if (arr[curP] == p) break;
			else if (curN > 0)
			{
				curN --;
				arr[curP] ++;
			}
			else
			{
				curN = arr[curP];
				arr[curP] = 0;
			}
			curP = (curP+1) % n;
		}
		printf("%d\n", curP);
	}
}
7675

题意:求联通块的数目

思路:广搜深搜皆可

#include <cstdio>
#include <queue>
using namespace std;

int w, h, ans;
bool g[55][55];
int dir[8][2] = {{-1,-1},{-1,0},{-1,1},{0,1},{0,-1},{1,-1},{1,0},{1,1}};
struct Point
{
	int r, c;
	Point() {}
	Point(int _r, int _c): r(_r), c(_c) {}
	Point next(int k) { return Point(r+dir[k][0], c+dir[k][1]); }
	bool leagal() { return r>=0 && r<h && c>=0 && c<w && g[r][c]==1; }
	void setSea() { g[r][c] = 0; }
} cur, nxt;
queue <Point> Q;
int main()
{
	while (true)
	{
		scanf("%d%d",&w,&h);
		if (w == 0 && h == 0) break;
		for (int i = 0; i < h; ++ i)
			for (int j = 0; j < w; ++ j)
				scanf("%d",&g[i][j]);
		ans = 0;
		for (int i = 0; i < h; ++ i)
		{
			for (int j = 0; j < w; ++ j)
			{
				if (g[i][j] == 1)
				{
					ans ++;
					cur = Point(i,j);
					cur.setSea();
					Q.push(cur);
					while (!Q.empty())
					{
						cur = Q.front();
						Q.pop();
						for (int k = 0; k < 8; ++ k)
						{
							nxt = cur.next(k);
							if (nxt.leagal())
							{
								nxt.setSea();
								Q.push(nxt);
							}
						}
					}
				}
			}
		}
		printf("%d\n", ans);
	}
}
7691

题意:给一个字母的等式,每个字母代表不同的数字,每个数字被不同的字母代表着,不能有除零外的以零打头的数,问有多少种字母的复制方法。

思路:记录每一个字母的权值,(在十位的字母权值+10,类推)。然后深搜,注意打头的字母不能为零的情况。这样暴力深搜会超时(我做的时候是超时了)。加一个非常简单的剪枝,就是在搜之前判断当前最大和最小的可能值,如果最大值也比零小或者最小值也比零大,那么不必再搜索下去。

#include <cstdio>
#include <cstdlib>
#include <cstring>

char s[15];
int n, c[30], l, ans;
int data[30], numb[30];
bool used[30], frst[30], vis[15];
void dfs(int d, int sum)
{
	int x = sum;
	for (int i = d; i < l; ++ i)
		if (c[data[i]] > 0) x += c[data[i]] * 9;
	if (x < 0) return ;
	x = sum;
	for (int i = d; i < l; ++ i)
		if (c[data[i]] < 0) x += c[data[i]] * 9;
	if (x > 0) return ;
	if (d == l)
	{
		if (sum == 0) ans ++;
		return ;
	}
	for (int i = 0; i < 10; ++ i) if (!vis[i])
	{
		if (frst[data[d]] == 1 && i == 0) continue;
		numb[d] = i;
		vis[i] = true;
		dfs(d+1, sum+c[data[d]]*i);
		vis[i] = false;
	}
}
int main()
{
	while (scanf("%d", &n), n)
	{
		memset(used, false, sizeof(used));
		memset(frst, false, sizeof(frst));
		memset(c, 0, sizeof(c));
		for (int i = 0; i < n; ++ i)
		{
			scanf("%s", s);
			l = strlen(s);
			if (l > 1) frst[s[0]-'A'] = true;
			for (int j = l-1, pow10 = 1; j >= 0; -- j, pow10 *= 10)
			{
				used[s[j]-'A'] = true;
				c[s[j]-'A'] += ((i==n-1) ? (-pow10) : pow10);
			}
		}
		l = 0;
		for (int i = 0; i < 26; ++ i)
			if (used[i]) data[l++] = i;
		ans = 0;
		dfs(0, 0);
		printf("%d\n", ans);
	}
}
7692

题意:给一个n个城市m条路径(有速度上限,有权值)的无向图,给出起点终点,每经过一个城市,速度可以+1,+0,-1,问从起点到终点的最短时间。

思路:直接广搜,可以用优先队列,速度上限最大不过30,城市最多不过30个,很好判重。值得注意的题目的一个信息是:不能走U字型的路,被这个坑了很多时间,也就是不能1-2-1这样走。

#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
#define INF 1e20
#define N 55
struct Edge
{
	int ed, dist, limit;
	Edge() {}
	Edge(int _e, int _d, int _l): ed(_e), dist(_d), limit(_l) {}
};
vector <Edge> adj[N];
double best[N][N][N];
bool visit[N][N][N];
int n, m, s, e;
struct Node
{
	int preP, curP, v;
	double t;
	Node() {}
	Node(int _pp, int _cp, int _v, double _t): preP(_pp), curP(_cp), v(_v), t(_t) {}
	bool operator < (const Node &obj) const { return t > obj.t;}
}cur, nxt;
bool bfs()
{
	priority_queue <Node> Q;
	Q.push(Node(s,s,0,0));
	best[s][0][s] = 0;
	while (!Q.empty())
	{
		cur = Q.top();
		Q.pop();
		if (visit[cur.curP][cur.v][cur.preP]) continue;
		else visit[cur.curP][cur.v][cur.preP] = true;
		if (cur.curP==e && cur.v==1)
		{
			printf("%.5lf\n", cur.t);
			return true;
		}
		nxt.preP = cur.curP;
		for (int i = adj[cur.curP].size()-1; i >= 0; -- i)
		{
			nxt.curP = adj[cur.curP][i].ed;
			if (nxt.curP == cur.preP) continue;
			for (int dv = -1; dv <= 1; ++ dv)
			{
				nxt.v = cur.v+dv;
				if (!visit[nxt.curP][nxt.v][nxt.preP] && nxt.v>0 && nxt.v<=adj[cur.curP][i].limit)
				{
					nxt.t = cur.t + adj[cur.curP][i].dist*1.0/nxt.v;
					if (best[nxt.curP][nxt.v][nxt.preP] > nxt.t)
					{
						best[nxt.curP][nxt.v][nxt.preP] = nxt.t;
						Q.push(nxt);
					}
				}
			}
		}
	}
	return false;
}
int main()
{
	while (scanf("%d%d",&n,&m) != EOF)
	{
		if (n==0 && m==0) break;
		scanf("%d%d",&s,&e);
		for (int i = 0; i < N; ++ i)
		{
			adj[i].clear();
			for (int j = 0; j < N; ++ j)
			{
				for (int k = 0; k < N; ++ k)
				{
					visit[i][j][k] = false;
					best[i][j][k] = INF;
				}
			}
		}
		for (int i=0,u,v,d,c; i < m; ++ i)
		{
			scanf("%d%d%d%d",&u,&v,&d,&c);
			adj[u].push_back(Edge(v,d,c));
			adj[v].push_back(Edge(u,d,c));
		}
		if (!bfs()) puts("unreachable");
	}
}
7693

题意:给两堆数{X},{Y},如果{X}中的某个数和{Y}中的某个数的GCD大于1,那么称这两个数可以配对,问最多有多少个配对。

思路:赤裸裸的二分图匹配问题。

#include <cstdio>
#include <cstring>
#define N 505
int nx, ny;
int cx[N], cy[N];
bool bmap[N][N];
bool bmask[N];
bool gcd(int a, int b)
{
	int mod;
	while (mod = a % b)
	{
		a = b;
		b = mod;
	}
	return b > 1;
}
int findPath(int u)
{
	for (int i = 0; i < ny; ++ i)
	{
		if (bmap[u][i] && !bmask[i])
		{
			bmask[i] = 1;
			if (cy[i] == -1 || findPath(cy[i]))
			{
				cy[i] = u;
				cx[u] = i;
				return 1;
			}
		}
	}
	return 0;
}
int maxMatch()
{
	int res = 0;
	memset(cx, -1, sizeof(cx));
	memset(cy, -1, sizeof(cy));
	for (int i = 0; i < nx; ++ i)
	{
		if (cx[i] == -1)
		{
			memset(bmask, 0, sizeof(bmask));
			res += findPath(i);
		}
	}
	return res;
}
int main()
{
	while (true)
	{
		scanf("%d%d",&nx,&ny);
		if (nx == 0 && ny == 0) break;
		memset(bmap, 0, sizeof(bmap));
		for (int i = 0; i < nx; ++ i)
			scanf("%d", &cx[i]);
		for (int i = 0; i < ny; ++ i)
		{
			scanf("%d", &cy[i]);
			for (int j = 0; j < nx; ++ j)
			{
				if (gcd(cy[i], cx[j]))
					bmap[j][i] = 1;
			}
		}
		printf("%d\n", maxMatch());
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值