soj 2013 weekly-3 暨校赛模拟I 7763-7770

7763

题意:有m份彩票,n个人,已知第i份彩票的奖金是 2^i 元,已知每个人买的不同彩票的数量,已知每张彩票中奖的概率是相等的,问对于每个人来说,得到的奖金比别的所有人的奖金都多的概率。

思路:看起来很吓人要算这样奇葩的概率。。。但是2^m > 2^(m-1) + ... + 2^1。。。所以最后的概率就是该人买的第m份彩票中奖的概率。。。

#include <cstdio>
#define N 10005
int n, m, useless;
int total, p[N];
int gcd(int a, int b)
{
	if (a == 0) return b;
	if (b == 0) return a;
	int mod;
	while (mod = a % b)
	{
		a = b;
		b = mod;
	}
	return b;
}
int main()
{
	while (true)
	{
		scanf("%d%d",&n,&m);
		if (n == 0 && m == 0) break;
		total = 0;
		for (int i = 0; i < n; ++ i)
		{
			for (int j = 0; j < m; ++ j)
				scanf("%d",&useless);
			total += useless;
			p[i] = useless;
		}
		for (int i = 0; i < n; ++ i)
		{
			useless = gcd(p[i], total);
			p[i] /= useless;
			printf("%d / %d\n", p[i], total/useless);
		}
	}
}
7764:

题意:n个政党,给出他们的名字,和相应的得票率(一位的浮点数)。再给出g个猜测,猜测的格式是 XX + YY + ... = n,n是整数,XX,YY是政党名字,问这些猜测是否正确。

思路:坑爹的是浮点数是会有误差的,然后由于是一位的浮点数。。。我就采用了最无聊的办法。。。把那位小数和整数单独存起来。。。应该可以用其他的例如允许eps之类的吧。。。当时懒得了。。。

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define eps 1e-6
int p, g, n, flag;
string par[55], tstr;
int z[55], f[55], tz, tf;
int index(string &st)
{
	for (int ret = 0; ret < p; ++ ret)
		if (par[ret] == st) return ret;
	return -1;
}
int main()
{
	while (scanf("%d%d",&p,&g) != EOF)
	{
		for (int i = 0; i < p; ++ i)
		{
			cin >> par[i];
			scanf("%d.%d",&z[i],&f[i]);
		}
		for (int Case = 1; Case <= g; ++ Case)
		{
			cin >> tstr;
			tz = z[index(tstr)];
			tf = f[index(tstr)];
			while (true)
			{
				cin >> tstr;
				if (tstr == "+")
				{
					cin >> tstr;
					tz += z[index(tstr)];
					tf += f[index(tstr)];
				}
				else if (tstr == "<")
				{
					tz += tf / 10;
					tf %= 10;
					cin >> n;
					flag = tz<n ? 1 : 0;
					break;
				}
				else if (tstr == ">")
				{
					tz += tf / 10;
					tf %= 10;
					cin >> n;
					flag = tz>n || (tz==n&&tf!=0) ? 1 : 0;
					break;
				}
				else if (tstr == ">=")
				{
					tz += tf / 10;
					tf %= 10;
					cin >> n;
					flag = tz>=n ? 1 : 0;
					break;
				}
				else if (tstr == "<=")
				{
					tz += tf / 10;
					tf %= 10;
					cin >> n;
					flag = tz<n || (tz==n&&tf==0) ? 1 : 0;
					break;
				}
				else if (tstr == "=")
				{
					tz += tf / 10;
					tf %= 10;
					cin >> n;
					flag = tz==n&&tf==0 ? 1 : 0;
					break;
				}
			}
			printf("Guess #%d was %s.\n", Case, flag?"correct":"incorrect");
		}
	}
}
7765:

题意:求1~n的一个排列,满足两个性质:(1)完全不单调性,即对于任意连续的三个数,中间的数最大或者最小。(2)唯一循环性,也就是从第一个数开始,把它当作位置的指针,依次指下去,会遍历这n个数回到第一个数。

思路:开始没人做,坑爹啊,后来发现很水有木有。。。明明是稍加构造直接okay的。。。

#include <cstdio>
int n, ans[1000005];
int main()
{
	while (scanf("%d",&n), n)
	{
		ans[1] = 1;
		for (int i = 2; i <= n; ++ i)
		{
			if (i&1)
			{
				ans[i] = ans[i-2];
				ans[i-2] = i;
			}
			else
			{
				ans[i] = ans[i-1];
				ans[i-1] = i;
			}
		}
		for (int i = 1; i <= n; ++ i)
			printf("%d%c", ans[i], i==n?'\n':' ');
	}
}
7766:

思路:如题,裸的最小生成树

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

const int MAXN = 200005;
int m, n, ans;
int root[MAXN];
void init() {
    for (int i = 0; i < MAXN; ++ i)
        root[i] = i;
}
int Find(int x) {
    int t, r = root[x];
    while (root[r] != r) r = root[r];
    while (root[x] != r) {
        t = root[x];
        root[x] = r;
        x = t;
    }
    return r;
}
void Merge(int x, int y) {
    int fx = Find(x), fy = Find(y);
    if (fx != fy) root[fx] = fy;
}

struct Edge {
    int st, ed, len;
    Edge() {}
    Edge(int _s, int _e, int _l): st(_s), ed(_e), len(_l) {}
    bool operator < (const Edge &e) const {
        return len < e.len;
    }
    bool operator > (const Edge &e) const {
        return len > e.len;
    }
};

priority_queue<Edge, vector<Edge>, greater<Edge> > Q;
int Kruskal() {
    int ans = 0;
    init();
    Edge e;
    int fx, fy;
    while (!Q.empty()) {
        e = Q.top();
        Q.pop();
        fx = Find(e.st);
        fy = Find(e.ed);
        if (fx != fy) {
            Merge(e.st, e.ed);
			ans += e.len;
        }
    }
    return ans;
}

int main()
{
	while (true)
	{
		scanf("%d%d",&n,&m);
		if (n==0 && m==0) break;
		ans = 0;
		init();
		for (int i=0, st, ed, len; i < m; ++ i)
		{
			scanf("%d%d%d",&st,&ed,&len);
			ans += len;
			Q.push(Edge(st,ed,len));
		}
		printf("%d\n", ans-Kruskal());
	}
}

7767:(!)

题意:没看

思路:等题解

7768:(!)

题意:n个学生吃饭,他们的饭量y[1,...,n]已给出,而每一份饭的份量未知,给出常数a,b,定义学生的消耗 = a*浪费的饭量 + b*打饭的次数。要求每个学生打饭不超过三次,问学生的消耗和最少是多少。

思路:(TLE)我想,学生只有1000个的话,那么每份饭的分量就只有y[i]/1,y[i]/2,y[i]/3这3000种可能,那么对这3000种份量计算总学生的消耗,就是3000*1000次计算,超时,可以二分查找最少的份量(因为每个学生打饭不超过三次,必定有一个份量下界),只计算前若干种(大约50种?)情况,但还是超时。

7769:

题意:给出一个生成随机数的方法(对于一个四位数,不断的平方,取中间四位数),问给出一个最初数的情况下最多能产生多少个“随机数”?

思路:如题

#include <cstdio>
#include <cstring>
bool vis[10005];
int ans, n;
int main()
{
	while (scanf("%d",&n) && n)
	{
		memset(vis, 0, sizeof(vis));
		ans = 0;
		while (!vis[n])
		{
			ans ++;
			vis[n] = true;
			n = (n*n/100) % 10000;
		}
		printf("%d\n", ans);
	}
}
7770:

题意:有n(上限10000)个城市,h(上限100)个城市有旅馆,现在要从城市1到达城市n。给出m(100000)条路径,每条都是双向的路经,从a到b的时间t。已知不能不休息地行驶600分钟,问最少要休息多少次才能到达。

思路:做SPFA,直接TLE,分析原因。

发现如果两个城市之间有不超过600的路径且都有旅馆,就会在两个城市之间来回(休息天数不断增多),这样是徒劳的,要避免这种状况,就对每个状态增加了是否在这100个旅馆休息过(显然每个旅馆最多只可能待一夜,如果是最佳方案的话)。

这样的话还是会有问题,就是如果两个城市之间存在长度为1的路径,那么就会在两个城市之间来来回回,于是再增加筛选条件,就是休息了d次到达k城市的最少连续行驶时间,如果要推入的状态比这个大,那么直接舍弃。

代码很渣, 因为是不断修改的。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define N 10005
int n, h, m;
bool hot[105];
int pnt[N]; // hotel
struct Edge
{
	int e, w;
	Edge() {}
	Edge(int _e, int _w): e(_e), w(_w) {}
};
vector <Edge> adj[N];
struct Item
{
	int d, t, l;
	bool vis[105];
	Item() { memset(vis,0,sizeof(vis)); }
	Item(int _d, int _t, int _l): d(_d), t(_t), l(_l) { memset(vis,0,sizeof(vis)); }
	bool operator < (const Item &ob) const
	{
		if (d != ob.d) return d > ob.d;
		if (t != ob.t) return t > ob.t;
		return l != n;
	}
	void print()
	{
		printf("%d %d %d ", l, t, d);
		for (int i = 0; i < 4; ++ i) printf("%d",vis[i]);
		printf("\n");
	}
} cur, nxt;
int best[N][105];
int calc()
{
	priority_queue <Item> Q;
	Q.push(Item(0,0,1));
	best[1][0] = 0;
	while (!Q.empty())
	{
		cur = Q.top();
		//cur.print();
		Q.pop();
		if (cur.l == n) return cur.d;
		if (best[cur.l][cur.d] != cur.t) continue;
		for (int i = 0; i < adj[cur.l].size(); ++ i)
		{
			nxt = cur;
			nxt.l = adj[cur.l][i].e;
			nxt.t = cur.t + adj[cur.l][i].w;
			if (nxt.t > 600)
			{
				if (cur.t != 0 && pnt[cur.l]>0 && !nxt.vis[pnt[cur.l]])
				{
					nxt.d ++;
					nxt.l = cur.l;
					nxt.t = 0;
					nxt.vis[pnt[cur.l]] = true;
					best[nxt.l][nxt.d] = 0;
					Q.push(nxt);
				}
			}
			else
			{
				if (best[nxt.l][nxt.d] > nxt.t)
				{
					Q.push(nxt);
					best[nxt.l][nxt.d] = nxt.t;
				}
			}
		}
	}
	return -1;
}
int main()
{
	while (scanf("%d",&n), n)
	{
		for (int i = 1; i <= n; ++ i)
		{
			adj[i].clear();
			for (int j = 0; j < 105; ++ j)
				best[i][j] = 601;
			pnt[i] = -1;
		}
		scanf("%d",&h);
		for (int i=0, cnt=1, c; i < h; ++ i)
		{
			scanf("%d",&c);
			pnt[c] = cnt++;
		}
		scanf("%d",&m);
		for (int i=0, st, ed, w; i < m; ++ i)
		{
			scanf("%d%d%d",&st,&ed,&w);
			adj[st].push_back(Edge(ed,w));
			adj[ed].push_back(Edge(st,w));
		}
		printf("%d\n", calc());
	}
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值