zoukankan      html  css  js  c++  java
  • 广工2016校赛决赛

    重现补的题目。

     

    Problem A: Krito的讨伐


    思路:不要求一次性杀光一个节点里面的所有怪物。 所以我们能够用一个优先队列。优先去杀那些我们当前能够挑战的。然后注意下处理一个房间可能有多个怪物或者无怪物。当我们杀完第x个房间的怪物时候。那么就把x的下一层的怪物增加队列,假设x的下一层出现了空房间[即房间不存在怪物],那么再把该房间当做新的x,继续增加新x的下一层直到出现了有怪物的房间位置。

    #define _CRT_SECURE_NO_DEPRECATE
    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<vector>
    #include<queue>
    #include<functional>
    #include<cstring>
    #include<string>
    using namespace std;
    typedef long long int LL;
    const int MAXN = 1005;
    struct Node
    {//怪物
    	int id, def, add_atk; //所在房间,防御值。击杀额外获得的攻击力
    };
    struct cmp
    {//按防御值最低而且额外值最高排序[优先队列]
    	bool operator ()(const Node &a, const Node &b)
    	{
    		if (a.def == b.def)
    		{
    			return a.add_atk < b.add_atk;
    		}
    		return a.def > b.def;
    	}
    };
    bool cmp0(Node a, Node b)
    { //按防御值最低而且额外值最高排序[预处理第0号房间]
    	if (a.def == b.def)
    	{
    		return a.add_atk > b.add_atk;
    	}
    	return a.def < b.def;
    }
    vector<Node>GW[MAXN]; //GW[i]:第i个房间的怪物情况
    vector<int>G[MAXN]; //图。保存树的结构
    int n, m, t, atk, num[MAXN], vis[MAXN];
    //点数。怪物数,例子数。每一个房间的怪物数。訪问数组
    bool bfs()
    {
    	priority_queue<Node, vector<Node>, cmp>Q; //怪物队列
    	queue<int>inq; //扩展队列,保存x,即保存能扩展下一层的房间号
    	inq.push(0); vis[0] = 1;
    	while (!inq.empty())
    	{
    		int top = inq.front(); inq.pop();
    		if (GW[top].size()) //非空房间
    		{
    			for (int i = 0; i < GW[top].size(); i++)
    			{ //增加此房间的怪物到怪物队列
    				Q.push(GW[top][i]);
    			}
    		}
    		else //此房间为空房间
    		{
    			for (int i = 0; i < G[top].size(); i++)
    			{ //继续想inq队列增加下一层结点。继续扩展
    				if (vis[G[top][i]] == 0)
    				{
    					vis[G[top][i]] = 1;
    					inq.push(G[top][i]);
    				}
    			}
    		}
    	}
    	while (!Q.empty())
    	{
    		Node front = Q.top(); Q.pop();
    		if (atk <= front.def) //当前最优都无法击杀。那么后面肯定击杀不了。

    { return false; } atk += front.add_atk; num[front.id]--; if (num[front.id] == 0) //此房间的怪物已经击杀完或者是空房间 { for (int i = 0; i < G[front.id].size(); i++) //同上处理 { if (vis[G[front.id][i]] == 0) { vis[G[front.id][i]] = 1; inq.push(G[front.id][i]); } } while (!inq.empty()) { int top = inq.front(); inq.pop(); if (GW[top].size()) { for (int i = 0; i < GW[top].size(); i++) { Q.push(GW[top][i]); } } else { for (int i = 0; i < G[top].size(); i++) { if (vis[G[top][i]] == 0) { vis[G[top][i]] = 1; inq.push(G[top][i]); } } } } } } return true; //所有怪物击杀完毕 } void init() { memset(num, 0, sizeof(num)); memset(vis, 0, sizeof(vis)); for (int i = 0; i <= n; i++) { G[i].clear(); GW[i].clear(); } } int main() { scanf("%d", &t); while (t--) { init(); scanf("%d%d", &n, &m); for (int i = 0; i < n - 1; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } scanf("%d", &atk); for (int i = 0; i < m; i++) { Node temp; scanf("%d%d%d", &temp.id, &temp.def, &temp.add_atk); GW[temp.id].push_back(temp); num[temp.id]++; } if (bfs()) { printf("Oh yes. "); } else { printf("Good Good Study,Day Day Up. "); } } return 0; }


    Problem B: Sward Art Online

    思路:简单分组背包[事实上分完组能够用贪心来求],一共同拥有4种装备。各自是头盔。首饰,单手武器,双杀武器。

    那么我们能够把这些装备分成2组,即防具和武器。由于每组装备仅仅能选一个,那么能够这么定义,防具组:仅仅选头盔,仅仅选首饰,头盔和首饰组合[注意部分有buff加成]。 武器:仅仅选一个单手武器。 仅仅选2个单手武器[组成双手武器],仅仅选双手武器。

       然后就是分成2组简单的01背包。

    之后就是01背包的求解。    事实上能够不用背包:由于仅仅有2组。

    那么我们能够在限有的金币下,在x=仅仅选防具的最大攻击力,在y=仅仅选武器的最大攻击力,在z=选防具和武器搭配的最大攻击力.   终于结果就是max(x,y,z)

    #define _CRT_SECURE_NO_DEPRECATE
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<stdio.h>
    #include<set>
    #include<vector>
    using namespace std;
    typedef long long int LL;
    const int MAXN = 10000 + 5;
    const int MAXZ = 105;
    struct Node
    {
    	int w, v; //价格,攻击力
    	int id, buff; //是否有buff加成
    	Node(int b = 0, int c = 0, int d = -1, int e = 0) :w(b), v(c), id(d), buff(c){};
    	void init(int a = 0, int b = 0, int c = -1, int d = -1)
    	{
    		w = a; v = b; id = c; buff = d;
    	}
    };
    Node Tk[MAXZ], Ss[MAXZ], Dw[MAXZ], Sw[MAXZ], TS[3][MAXN];
    //头盔。首饰,单手武器,双手武器。TS[1]:防具组,TS[2]:武器组
    int main()
    {
    	int t;
    	scanf("%d", &t);
    	while (t--)
    	{
    		int m, a, b, c, d, ts = 0, ts2 = 0;
    		int wei, val, Buff, Id;
    		scanf("%d%d%d%d%d", &m, &a, &b, &c, &d);
    		for (int i = 0; i<a; i++) //头盔
    		{
    			scanf("%d%d", &wei, &val);
    			Node temp(wei, val, 0, 0);
    			Tk[i] = temp;
    			TS[1][ts++] = temp; //防具组:仅仅选一个头盔
    		}
    		for (int i = 0; i<b; i++) //首饰
    		{
    			scanf("%d%d%d%d", &wei, &val, &Id, &Buff);
    			Node temp(Node(wei, val, Id, Buff));
    			Ss[i] = temp;
    			TS[1][ts++] = temp;  //防具组:仅仅选一个首饰
    			if (Id == -1 || Buff <= 0)
    			{
    				continue;
    			}
    			temp.init(Ss[i].w + Tk[Id].w, Ss[i].v + Tk[Id].v + Buff, 0, 0);
    			TS[1][ts++] = temp; //防具组:有buff加成的头盔+首饰
    		}
    		for (int i = 0; i<a; i++) //防具组:头盔+首饰
    		{
    			for (int j = 0; j<b; j++)
    			{
    				Node temp(Node(Tk[i].w + Ss[j].w, Tk[i].v + Ss[j].v, 0, 0));
    				TS[1][ts++] = temp;
    			}
    		}
    		for (int i = 0; i<c; i++) //单手武器
    		{
    			scanf("%d%d", &wei, &val);
    			Node temp(wei, val, 0, 0);
    			TS[2][ts2++] = temp; //武器组:仅仅选一个单手武器
    			Dw[i] = temp;
    		}
    		for (int i = 0; i<c; i++) //武器组:一个单手武器+一个单手武器=双手武器
    		{
    			for (int j = i + 1; j<c; j++)//由于每种仅仅有一个,所以j要从i+1開始
    			{
    				Node temp(Dw[i].w + Dw[j].w, Dw[i].v + Dw[j].v);
    				TS[2][ts2++] = temp;
    			}
    		}
    		for (int i = 0; i<d; i++) //双手武器
    		{
    			scanf("%d%d", &wei, &val);
    			Node temp(wei, val, 0, 0);
    			TS[2][ts2++] = temp; //武器组:仅仅选一个双手武器。
    		}
    		//贪心求法:
    		LL ans = 0; 
    		for (int i = 0; i<ts; i++)
    		{//在限有的金币下,x=仅仅选防具的最大攻击力
    			if (TS[1][i].w <= m) //在限有的金币下
    			{
    				ans = max(ans, 1LL * TS[1][i].v);
    			}
    		}
    		for (int i = 0; i<ts2; i++)
    		{ //在限有的金币下。y=仅仅选武器的最大攻击力
    			if (TS[2][i].w <= m) //在限有的金币下
    			{
    				ans = max(ans, 1LL * TS[2][i].v);
    			}
    		}
    		for (int i = 0; i<ts; i++)
    		{//z=选防具和武器搭配的最大攻击力
    			for (int j = 0; j<ts2; j++)
    			{
    				if (TS[1][i].w + TS[2][j].w <= m) //在限有的金币下
    				{
    					ans = max(ans, 1LL * (TS[1][i].v + TS[2][j].v));
    				}
    			}
    		}
    		printf("%lld
    ", ans); //ans=max(x,y,z)
    
    		//////////////////////////分组背包求法//////////////////////////////////
    		int dp[3][MAXN];
    		memset(dp, 0, sizeof(dp));
    		for (int i = 1; i <= 2; i++)
    		{
    			for (int k = 0; k <= m; k++)
    			{
    				dp[i][k] = dp[i - 1][k];
    			}
    			for (int k = 0; k<(i == 1 ? ts : ts2); k++)
    			{
    				for (int j = m; j >= TS[i][k].w; j--)
    				{
    					dp[i][j] = max(dp[i][j], dp[i - 1][j - TS[i][k].w] + TS[i][k].v);
    				}
    			}
    		}
    		printf("%d
    ", dp[2][m]);
    	}
    	return 0;
    }


    Problem C: wintermelon的魔界寻路之旅

    思路:先求出最短路长度,然后dfs记忆化搜索满足最短路的路径个数。 我们令D[i][j]为从位置(1,1)到位置(i,j)的最短路长度+相应关于对角线对称的长度,那么D[X][Y](X+Y==n+1)即到达对角线的时候就是真实的从(1,1)到终点(n,n)的长度。

    然后就是从对角线沿着最短路走到(1,1)的路径计数了。

    详细看代码吧。

    #define _CRT_SECURE_NO_DEPRECATE
    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<string>
    #include<cstring>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long int LL;
    const int MAXN = 100 + 5;
    const int INF = 0x3f3f3f3f;
    const int MOD = 1000000009;
    int D[MAXN][MAXN], g[MAXN][MAXN];
    //D:起点到其它点的最短路距离
    int n, minval, dist[4][2] = { 0, 1, 0, -1, 1, 0, -1, 0 };
    //minval:起点到终点的最短路距离,dist:方向向量
    struct Node
    {
    	int x, y;
    	int val;
    };
    struct cmp
    {
    	bool operator()(const Node &a, const Node &b)
    	{
    		return a.val > b.val;
    	}
    };
    bool check(int x, int y)//是否越界
    {
    	return x >= 1 && x <= n&&y >= 1 && y <= n;
    }
    void bfs(Node start)
    {
    	priority_queue<Node, vector<Node>, cmp>Q;
    	memset(D, -1, sizeof(D));
    	Q.push(start);
    	D[start.x][start.y] = start.val;
    	while (!Q.empty())
    	{
    		Node t = Q.top(); Q.pop();
    		Node next;
    		for (int i = 0; i < 4; i++)
    		{
    			next.x = t.x + dist[i][0];
    			next.y = t.y + dist[i][1];
    			if (check(next.x, next.y) && D[next.x][next.y] == -1 && next.x + next.y <= n + 1)
    			{ //g[next.x][next.y]为当前值,g[n - next.y + 1][n - next.x + 1]为对角线对称点值
    				next.val = t.val + g[next.x][next.y] + (next.x + next.y == n + 1 ?

    0 : g[n - next.y + 1][n - next.x + 1]); D[next.x][next.y] = next.val; Q.push(next); } } } minval = INF; for (int i = 1; i <= n; i++)//找到最短路 { minval = min(minval, D[i][n + 1 - i]); } } int dp[MAXN][MAXN]; int dfs(int x, int y) { if (dp[x][y] != -1)//记忆化搜索 { return dp[x][y]; } int nextx, nexty, cnt = 0; for (int i = 0; i < 4; i++) { nextx = x + dist[i][0]; nexty = y + dist[i][1]; if (check(nextx, nexty) && nextx + nexty <= n + 1 && D[nextx][nexty] + g[x][y] + (x + y == n + 1 ? 0 : g[n - y + 1][n - x + 1]) == D[x][y]) { cnt = (cnt + dfs(nextx, nexty)) % MOD; } } dp[x][y] = cnt%MOD; return dp[x][y]; } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { scanf("%d", &g[i][j]); } } Node start; start.x = 1; start.y = 1, start.val = g[1][1] + g[n][n]; //起点。 bfs(start); //求(1,1)到其它点的最短路距离 /*for (int i = 1; i <= n; i++) //Debug { for (int j = 1; j <= n; j++) { printf("%d ", D[i][j]); } printf(" "); }*/ memset(dp, -1, sizeof(dp)); LL ans = 0; dp[1][1] = 1; for (int i = 1; i <= n; i++) { if (D[i][n + 1 - i] == minval) { ans = (ans + dfs(i, n + 1 - i)) % MOD; } } printf("%lld ", ans); } return 0; }


    Problem D: 二叉树的中序遍历

    思路:仅仅要出现两个连续的##就输出no,否则就输出yes。证明略,仅仅有多画几个就能找到规律

    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<stdio.h>
    #include<set>
    using namespace std;
    typedef long long int LL;
    const int MAXN=100000+5;
    char str[MAXN];
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            scanf("%s",str); bool flag=true;
            for(int i=0;i<strlen(str)-1;i++)
            {
                if(str[i]=='#'&&str[i+1]=='#')
                {
                    flag=false;
                    break;
                }
            }
            printf(flag?"yes
    ":"no
    ");
        }
        return 0;
    }


    Problem E: 积木积水

    思路:能够发现能装到水的当且仅当形状呈u型,那么能够用一个栈保存一个高度递减的序列,当前高度大于栈顶高度,则能够装水。   关于装水能够用相似扫描线的思想。  该题也有dp的做法。

    #define _CRT_SECURE_NO_DEPRECATE
    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<string>
    #include<cstring>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long int LL;
    const int MAXN = 1000000 + 5;
    struct Node
    {
    	int H;
    	int id;
    }A[MAXN];
    void solve(int n)
    {
    	LL ans = 0;
    	stack<Node>st;
    	for (int i = 0; i < n; i++)
    	{
    		if (st.empty())
    		{
    			st.push(A[i]);
    		}
    		else
    		{
    			LL top_h = 0;
    			LL cnt = 0, tot = 0;
    			while (!st.empty())
    			{
    				Node tmp = st.top();
    				top_h = tmp.H;
    				if (A[i].H > tmp.H)
    				{
    					cnt += 1LL * (A[i].id - tmp.id);
    					tot += 1LL * (A[i].id - tmp.id)*tmp.H;
    					A[i].id = tmp.id;
    					st.pop();
    				}
    				else
    				{
    					break;
    				}
    			}
    			ans += 1LL * (cnt* min(1LL * A[i].H, top_h) - tot);
    			st.push(A[i]);
    		}
    	}
    	printf("%lld
    ", ans);
    }
    int main()
    {
    	int t;
    	scanf("%d", &t);
    	while (t--)
    	{
    		int n;
    		scanf("%d", &n);
    		for (int i = 0; i < n; i++)
    		{
    			scanf("%d", &A[i].H);
    			A[i].id = i;
    		}
    		solve(n);
    	}
    	return 0;
    }


    Problem F: 我是好人4

    思路:一看题意就知道是容斥,可是发现n<=50,一般容斥肯定TLE无误。所以考虑剪枝:1,当当前递归时的最小公倍数已经大于1e9就剪枝。

    能够发现递归层数不会超过10层[前10个质数相乘已经大于1e9了]。 2,对输入数据进行优化处理。当n个数中出现有倍数关系的则能够删掉。

    出现1时就输出0。

    加上这些优化就能过了。

     尽管能过可是还是能找到令该方法TLE的例子。

    比方输入50个数,这50个数都是互不相等的质数。那么也会TLE,可是题目有提示:数据是随机生成的,尔等尽可任意乱搞。 所以除非脸黑。一般不会遇到全是互不相等的质数的情况。

    #define _CRT_SECURE_NO_DEPRECATE
    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<string>
    #include<cstring>
    using namespace std;
    typedef long long int LL;
    const int MAXM = 1e9;
    LL p, ans;
    int n, cnt, num[55], temp[55], vis[55];
    LL gcd(LL x, LL y)
    {
    	return y ? gcd(y, x % y) : x;
    }
    void DFS(LL i, LL w, LL k)
    {//i:当前的位置。w:之前集合的最小公倍数,k:奇偶
    	if (w > MAXM) //剪枝。

    { return; } for (; i < cnt; i++) { p = num[i] / gcd(num[i], w) * w; ans += k*(MAXM / p); DFS(i + 1, p, -k); } } int main() { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); cnt = 0; bool one = false; memset(vis, 0, sizeof(vis)); for (int i = 0; i < n; i++) { scanf("%d", &temp[i]); if (temp[i] == 0) { vis[temp[i]] = 1; } if (temp[i] == 1)//出现1 { one = true; } } if (one) { printf("0 "); continue; } sort(temp, temp + n); for (int i = 0; i < n; i++) //删除里面的倍数 { for (int j = i + 1; j < n; j++) { if (vis[i] == 0 && vis[j] == 0 && temp[j] % temp[i] == 0) { vis[j] = 1; } } if (vis[i] == 0) { num[cnt++] = temp[i]; } } ans = 0; DFS(0, 1, 1); printf("%lld ", MAXM - ans); } }


    Problem G: 我是水题

    思路:就是水题。2333
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<stdio.h>
    #include<set>
    using namespace std;
    typedef long long int LL;
    const int MAXN = 10000;
    char str[MAXN];
    int main()
    {
    	int t;
    	scanf("%d", &t);
    	getchar();
    	while (t--)
    	{
    		gets(str);
    		set<char>word;
    		for (int i = 0; i<strlen(str); i++)
    		{
    			if (str[i] >= 'a'&&str[i] <= 'z'&&!word.count(str[i]))
    			{
    				word.insert(str[i]);
    			}
    		}
    		printf("%d
    ", word.size());
    	}
    	return 0;
    }


  • 相关阅读:
    玩转web之javaScript(五)---js和jquery一些不可不知的方法(input篇)
    设计模式 外观模式 一键电影模式
    设计模式 适配器模式 以手机充电器为例
    高仿微信5.2.1主界面架构 包含消息通知
    Java进阶 创建和销毁对象
    sql语句中单引号嵌套问题
    Spark SQL UDF和UDAF示例
    Spark Parquet使用
    iptables只允许指定ip访问本机的指定端口
    Spark On YARN内存和CPU分配
  • 原文地址:https://www.cnblogs.com/llguanli/p/8311741.html
Copyright © 2011-2022 走看看