UPC 2020年春混合个人训练第十八场

后天周末了,怀疑 “A.石子” 是单人游戏的 SG函数,刚好可以试试,实在不行只能厚着脸皮找大佬了

问题 D: 十字架
时间限制: 1 Sec 内存限制: 128 MB

题目描述
小D是虔诚的嘟嘟教徒。现在小G送他了一幅著名画家芬达奇的作品。这是
一幅n×n的作品,由”.”或者”#”构成,其中”.”相当于空白。但是现在小D怀疑小G送给他了一幅赝品。正版芬达奇的画作,是由若干个互不重叠的十字架拼起来的。每个十字架由五个”#”组成,如下:
.#.

.#.
而赝品则不能将所有的”#”分成若干个互不重叠的十字架,如:
.#…

.#…
特别地,如果一幅画里面全都是”.”,这仍然是一幅正品。
你的任务是帮助小D判断,小G送他的这幅画到底是不是正品。
输入
第一行一个数字n(1≤n≤100),含义如题目所述。
第2行到第n+1行,每行n个’.’或者’#’,描述整张画。
输出
一行,如果是正品,输出”YES”;如果是赝品,输出”NO”。
样例输入 Copy
【样例1】
5
.#…
####.
.####
…#.

【样例2】
4

样例输出 Copy
【样例1】
YES
【样例2】
NO

题解:这是一条简单的dfs。定义一个布尔数组判断能否构成十字架,构成十字架的依据是包括该点上下左右都是 #

#include <bits/stdc++.h>
using namespace std;

bool b[10100][10100]={0};
int n,i,j,x,y;

int dfs(int x,int y)
{ // 最后应该所有的是 0,有 1就表示有不合格的十字架,是赝品 
    if(x>=1 && y>=1 && x<=n && y<=n && b[x][y])
    { //能走的时候 
        if(b[x+1][y] && b[x-1][y] && b[x][1+y] && b[x][y-1]) //=1
        {  // 当能够组成一个完整的十字架的时候,把这个十字架的五个点标记为 0,表示走过了 
		    b[x][y] = 0;
            b[x+1][y] = 0;
            b[x-1][y] = 0;
            b[x][y+1] = 0;
            b[x][y-1] = 0;
        }
    }
}

int main()
{
    cin>>n;
    char ch;
    
    //输入时标记 
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            cin>>ch;
            if(ch=='.') b[i][j] = 0;
            if(ch=='#') b[i][j] = 1;  
        }
        
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(b[i][j]) dfs(i,j); //=1 
/* 如果是 dfs(1,1),会爆。 因为题目是要判断的是:
是否 “所有的 '#'都能分成若干个互不重叠的十字架 ”,
所以所有出发点 都要遍历一遍  */
         
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++){
            if(b[i][j]){
                cout<<"NO"; //dfs函数第一行已解释 
                return 0;
            }
        }
        
    cout<<"YES";  //同上注释 
    return 0;
}

问题 E: 小Z搭积木
时间限制: 1 Sec 内存限制: 128 MB

题目描述
小Z喜欢搭积木。小Z一共有n块积木,并且积木只能竖着一块一块的摞,可以摞多列。小Z的积木都是智能积木,第i块积木有一个情绪值Xi。当摞在该积木上面积木总数超过Xi时,i号积木就会不高兴。小Z情商这么高,肯定不希望有积木不高兴。但是他又希望每块积木都被用上,并且摞的积木列的总数最少。你能帮帮萌萌的小Z吗?
输入
第一行一个数字n,含义如题目所述。
第2行一共n个数,第i个数为Xi,含义如题目所述。
输出
输出一个数字,表示最小的积木列数目。
样例输入 Copy
【样例1】
3
0 0 10
【样例2】
4
0 0 0 0
样例输出 Copy
【样例1】
2
【样例2】
4
提示
30%数据,1≤n≤10
60%数据,1≤n≤100
80%数据,1≤n≤1000
100%数据,1≤n≤5000
对于所有数据点,都有Xi≤n

题解:这是一道模拟题(倒置的思想:从上往下看)。既然希望每块积木都被用上,那自然是能用的都用上。我们可以想到用贪心来解决这道题。首先按照情绪值。从小到大排序,将小的放在大的上面,能放就放,如果不能再放了,就需要再开一列,最后累加列数就是结果。

#include <bits/stdc++.h>
using namespace std;
int n,m,i,j,a[5005],v[5005],ans;

int main()
{
    cin>>n;
    for(i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1); //按情绪值大小升序排列 
    
    for(i=1;i<=n;i++)
	{
    	if(!v[i])
		{
            ans++; //没有达到情绪值时,一直放,算 1列,否则另开一列 
			m=1; //刚开始放的一块,累加的值表示当前最下面一块需要承受的情绪值 
            
            for(j=i+1;j<=n;j++)
              if(!v[j]&&a[j]>=m) //不合条件,无法继续循环时,ans++,另开一列 
                {v[j]=1;  m++;}  //已经放置的记为 1,标记一下 
        }
	}
    
    cout<<ans; //输出最小列数 
    return 0;
}

问题 F: 分宿舍
时间限制: 1 Sec 内存限制: 128 MB

题目描述
A校有着神奇的住宿制度,不分男女宿舍,所有n个学生被统一分到两栋宿舍楼中。作为年轻人,学生之间心生爱慕之情是很正常。我们用爱慕值来表示两名学生之间的爱慕程度,如果两名爱慕值为c的学生被安排在同一宿舍楼,他们或她们便会在一起,并造成影响力为c的早恋事件。
每年年末,身为政教处主任的你会将所有早恋事件按照影响力从大到小排成一个列表,然后上报给校长。公务繁忙的校长只会去看列表中第一个事件的影响力,如果影响很大,他会考虑撤换政教处主任。
在详细考察了n个学生之间的爱慕关系后,你觉得压力很大。你要合理的将学生们分到两栋宿舍,以求产生的早恋事件影响力都比较小,以保住自己的官职。假设只要处于同一栋宿舍楼的两个人之间有爱慕关系,他们就一定会在这年的某个时候在一起。
那么,要怎么分配,才能让校长看到的那个早恋事件的影响力最小呢?这个最小值是多少?
输入
第一行两个整数n和m,分别表示学生的数目和爱慕关系的对数。
接下来m行,每行为3个正整数ai,bi,ci,表示学生ai和bi之间有爱慕关系,爱慕值为ci。
数据保证1≤ai≤bi≤n,0<ci≤109,且每对爱慕关系只出现一次。
输出
输出一个数,为通过合理安排,校长看到的那个早恋事件的最小影响力。如果没有发生早恋事件,输出0。
样例输入 Copy
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
样例输出 Copy
3512
提示
对于100%的数据,n≤20000,m≤100000。

题解:这是一条二分匹配图的模板题【注意对于每一个点都要染色,因为这个图可能不连通 】。

// 二分匹配模板
void dfs(int x,int color)
{
   for(int i=0;i<g[x].size();i++)
   {
		if(!v[g[x][i]])
			dfs(g[x][i],3-color);
		
		else if(v[g[x][i]]==3-color) continue;
		else {
		    //不是二分图
		}
   }
} 
// AC代码
#include<bits/stdc++.h>
using namespace std;

int n,m,v[100010];
bool ok;
vector<pair<int,int> >g[20010];

void dfs(int x,int color,int t)
{
    if(ok==0) return ;
    v[x]=color;
    for(int i=0;i<g[x].size();i++)
	{
            if(g[x][i].second<=t) continue;
            if(v[g[x][i].first]==0)
                dfs(g[x][i].first,3-color,t);
            
            else if(v[g[x][i].first]==3-color) continue;
            else	{ok=0;  return;}
    }
}
    
bool check(int t) //二分的核心部分
{
    ok=1;
    memset(v,0,sizeof v);
    for(int i = 1; i <= n; i++) 
	    if(!v[i])  dfs(i,1,t);
	if(ok) return true;
	
	return false;
}

int main()
{
	cin>>n>>m;
	int mx=0;
	
	for(int i=1,a,b,c;i<=m;i++) 
	{
        scanf("%d%d%d",&a,&b,&c);
        g[a].push_back(make_pair(b,c));
        g[b].push_back(make_pair(a,c));
        mx=max(mx,c);
	}
	
	int l=0,r=mx,ans;
	
	while(l<r) //二分 
    {
		int mid=(l+r)>>1;
		if( check(mid) )   ans=mid,r=mid;
		else l=mid+1;
	}
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米莱虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值