杭电第九场 题解

比赛传送门:User Login (hdu.edu.cn)

1003Dota2 Pro Circuit

题目大意:给定每个队伍之前的得分ai,下一场可能得到的分数bi,求每一个队伍最好的与最坏的名次。

思路:贪心

分析:对于任意的一个队伍而言,它本身最好的得分是现在的得分加上下一场第一名的得分,最坏的情况是现在的得分加上下一场最后一名的得分。 要计算名次我们可以将其转换为分数,最好的名次,就是使比他本身分数更低的队伍尽可能多,最坏的同理,使比他分数高的队伍尽可能多。详情见代码注释。

代码:



#include<bits/stdc++.h>
using namespace std;
int t,n;
struct node{
    long long num,val;
}poi[5001]; 
int ans[5001][2];
long long b[5001];

//从小到大进行排序 
bool cmp(node a1,node a2)
{
    return a1.val<a2.val;
}

//输入并排序 
void init()
{

    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
	    scanf("%lld",&poi[i].val);
	    poi[i].num=i;
    }
    for(int i=1;i<=n;i++) scanf("%lld",&b[i]);
    sort(poi+1,poi+1+n,cmp);
    return;
}

//求最好的名次   尽量使得其他队伍分数更低 最大的分数给目前最小的队伍 
int maxn(int k)
{	//求解第k个队伍  最好得分是加上最大值 
    int N=poi[k].val+b[1],cnt=0;
	
	//双指针 p指向排序后的a数组 q指向b数组 
    int p=1;	
	int q=1;
	//当循环没有走完的时候 
	while(p<=n && q<=n){
		if(p==k){
			//第k个不必再加 跳过 
			p++;
			continue;
		} 
		//目前队伍得分比k小的时候 cnt++记录一共有多少更小的 
		if(poi[p].val+b[q]<=N){
			p++;
			cnt++;
		}
		q++;
	}
    //减去比自己分数少的队伍就是自己的名次 
    return n-cnt;
}

//求最差的名次 
int minn(int k)
{	
    int d=1,N=poi[k].val+b[n],ans=0;
    //ans超过当前的个数,N为队伍最少可以得分 

    for(int i=1;i<=n;i++){
        if(i==k) continue;
        if(poi[i].val>N){
        //如果不用加分数就已经超过一定在它前面 
            ans++;
            continue;
        } 
        else
        {	//加上更大 消耗一个d,d++ 
            if(poi[i].val+b[d]>N){
                ans++;
                d++;
            }
        }
    }
    return ans+1;
}

int main() // AC
{
    scanf("%d",&t);
    while(t>0)
    {
        t--;
        init();
        for(int i=1;i<=n;i++){
        	int number=poi[i].num;
        	ans[number][0]=maxn(i);
        	ans[number][1]=minn(i);
        }
        for(int i=1;i<=n;i++){
            printf("%d %d\n",ans[i][0],ans[i][1]);
        }
    }
    return 0;
 } 

1002Just another board game

题目大意:两个人下棋,棋盘的每个格子上有一个数字。先手可以同一行左右移动(可以不动),要让停留的数字最大;后手可以同一列上下移动(可以不动),要让停留的数字最小。轮到任何一方走时,他可以选择直接结束游戏,走k步游戏也会结束。

给出一个棋盘和一个固定大小k求输出的值。

分析:当k=1,只能有第一个操作一次,结果即为第一行上最大的数。

当k为偶数,当k>2时的结果是等价与k=2的。以k=4为例,每个人都可以选择随时停止游戏,当k=4对先手更为有利时,后手会选择在k=2时直接结束游戏,反之,对后手更有利的时候,先手会在k=1时候结束游戏,因此,只需要考虑k=2的时候即可。 当k=2,假设初始位置为s[x][0];那么有两种情况,要么,都不操作,结果即为s[x][0],要么先手会选择棋盘上列最小值最大的一列(后手必定会操作使得结果停留在那一列的最小值上),因此需要输出结果max(s[x][0],max(每列最小值))。

当k为奇数的时候,等价于k=3,原理同上。当k=3的时候,有三种情况,先手直接结束,后手直接结束和走完三步(最后一个人直接结束一定没有走完三步更优,因此一定不会有这种情况发生),假设开始的位置为s[x][0],直接结束为s[x][0],后手直接结束会停在这一行的最大值上,否则,会停在每一行的最大值最小的那个数上(后手没有结束游戏的时候,最后先手一定会操作,所以一定会停在一个行最大值上,因此后手必定会选择行最大值最小的一列),我们发现第二种和第三种情况都会停在行最大值上,因此第三种无疑是最优解决方案,直接考虑第一种与第三种即可,即输出max(s[x][0],max(行最大值最小的列的行最大值))

代码:

#include<bits/stdc++.h>
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
using namespace std;
const int N=1e6+10;
ll n,m,k;
ll min1[N];
ll maxline[N];
vector<ll> v[N];

void init(){
	for(int i=1;i<=n;i++){
		v[i].clear();
	}
}

int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	int t; cin>>t;
	while(t--){
		cin>>n>>m>>k;
		
		init();
		
		ll temp;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				cin>>temp;
				v[i].push_back(temp);
			}
		}
		
		ll max1=0;
		for(int i=0;i<m;i++){
			max1=max(max1,v[1][i]);
		}
		
		for(int i=1;i<=m;i++){
			min1[i]=1e9+10; // inf
		}
		for(int i=0;i<m;i++){
			for(int j=1;j<=n;j++){
				min1[i+1]=min(min1[i+1],v[j][i]);
			}
		}
		for(int i=1;i<=m;i++){
			maxline[i]=0;
		}
		for(int i=1;i<=n;i++){
			for(int j=0;j<m;j++){
				maxline[i]=max(maxline[i],v[i][j]);
			}
		}
		
		if(k==1){
			cout<<max1<<endl;
			continue;
		}
		
		// k>=2
		if(k%2==0){ // 相当于k==2 
			ll maxx=0;
			for(int i=2;i<=m;i++){
				maxx=max(maxx,min1[i]);
			}
			cout<<max(v[1][0],maxx)<<endl;
		}
		else{ // k>=3,奇数 
			ll minn=1e9+10;
			for(int i=1;i<=n;i++){
				minn=min(minn,maxline[i]);
			}
			cout<<max(v[1][0],minn)<<endl;
		}
	}
	return 0;
}

参考:杭电第九场 题解_Frank_Star的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这道题是一道典型的费用限制最短路题目,可以使用 Dijkstra 算法或者 SPFA 算法来解决。 具体思路如下: 1. 首先,我们需要读入输入数据。输入数据中包含了道路的数量、起点和终点,以及每条道路的起点、终点、长度和限制费用。 2. 接着,我们需要使用邻接表或邻接矩阵来存储图的信息。对于每条道路,我们可以将其起点和终点作为一个有向边的起点和终点,长度作为边权,限制费用作为边权的上界。 3. 然后,我们可以使用 Dijkstra 算法或 SPFA 算法求解从起点到终点的最短路径。在这个过程中,我们需要记录到每个点的最小费用和最小长度,以及更新每条边的最小费用和最小长度。 4. 最后,我们输出从起点到终点的最短路径长度即可。 需要注意的是,在使用 Dijkstra 算法或 SPFA 算法时,需要对每个点的最小费用和最小长度进行松弛操作。具体来说,当我们从一个点 u 经过一条边 (u,v) 到达另一个点 v 时,如果新的费用和长度比原来的小,则需要更新到达 v 的最小费用和最小长度,并将 v 加入到优先队列(Dijkstra 算法)或队列(SPFA 算法)中。 此外,还需要注意处理边权为 0 或负数的情况,以及处理无法到达终点的情况。 代码实现可以参考以下样例代码: ```c++ #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; const int MAXN = 1005, MAXM = 20005, INF = 0x3f3f3f3f; int n, m, s, t, cnt; int head[MAXN], dis[MAXN], vis[MAXN]; struct Edge { int v, w, c, nxt; } e[MAXM]; void addEdge(int u, int v, int w, int c) { e[++cnt].v = v, e[cnt].w = w, e[cnt].c = c, e[cnt].nxt = head[u], head[u] = cnt; } void dijkstra() { priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); dis[s] = 0; q.push(make_pair(0, s)); while (!q.empty()) { int u = q.top().second; q.pop(); if (vis[u]) continue; vis[u] = 1; for (int i = head[u]; i != -1; i = e[i].nxt) { int v = e[i].v, w = e[i].w, c = e[i].c; if (dis[u] + w < dis[v] && c >= dis[u] + w) { dis[v] = dis[u] + w; q.push(make_pair(dis[v], v)); } } } } int main() { memset(head, -1, sizeof(head)); scanf("%d %d %d %d", &n, &m, &s, &t); for (int i = 1; i <= m; i++) { int u, v, w, c; scanf("%d %d %d %d", &u, &v, &w, &c); addEdge(u, v, w, c); addEdge(v, u, w, c); } dijkstra(); if (dis[t] == INF) printf("-1\n"); else printf("%d\n", dis[t]); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值