dut周赛第二周

 

Problem E: So easy

Time Limit:3000/1000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)

Description

“iG牛逼, 登峰造极境!······“ GG同学仿佛还没有从月初iG夺冠的欢喜中醒过来,虽然还在睡梦中, 嘴里却不断的念叨着.不过, GG同学的梦没有做多久, 他被室友的一声大喊吵醒了, “出分了! 出分了! 面向对象编程出分了!”, GG同学打开微信小程序看了一下自己的成绩, 非常的失落. 新的一天就这样从兴奋的梦境跌落到悲伤的现实中. “唉, 没有对象又怎么面向对象编程呢?”

GG同学发明了一个新的游戏, 想要考考同样没有对象但是分数却很高的MM同学. 游戏规则是这样的: 最开始GG同学有一个 n×nn×n 的数组, 并且数组中的每一个元素都是0. 接下来GG同学会做若干次操作, 每次操作他会选择一行或者一列, 将他所选择的这一行(或者列)的所有元素都加上一个正整数. 经过几次操作之后, 他又将一个元素给隐藏了(即将该元素变为−1−1). 现在他想要MM同学告诉他这个元素是多少?

题解:假设h[i],l[j]分别为第i行第j列加的值

设maze[i][j]为-1 则maze[i][j]=h[i]+l[j],maze[i-1][j]=h[i-1]+l[j],maze[i][j-1]=h[i]+l[j-1],maze[i-1][j-1]=h[i-1]+l[j-1]

maze[i][j]=  h[i]+l[j]  

              =  (h[i-1]+l[j])+(h[i]+l[j-1])-(h[i-1]+l[j-1])  

              =  maze[i-1][j]+maze[i][j-1]-maze[i-1][j-1];

四个角落特判一下yi'f以防没有i-1和j-1(不过好像数据很水 不判也能A)

代码:

 

    #include<bits/stdc++.h>
using namespace std;
 
 
int a[1010][1010];
int main()
{
    int n;
    scanf("%d",&n);int ni,nj;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a[i][j]);
            if(a[i][j]==-1)
            {
                ni=i;nj=j;
            }
        }
    }
    if(ni==1&&nj==1)
    {
        printf("%d\n",a[1][2]+a[2][1]-a[2][2]);return 0;
    }
    if(ni==n&&nj==1)
    {
        printf("%d\n",a[n][2]+a[n-1][1]-a[n-1][1]);return 0;
    }
     
    if(ni==1&&nj==n)
    {
        printf("%d\n",a[1][n-1]+a[2][n]-a[2][n-1]);return 0;
    }
    printf("%d\n",a[ni][nj-1]+a[ni-1][nj]-a[ni-1][nj-1]);
}

Problem F: IG重回DotA2

Time Limit:3000/1000 MS (Java/Others)   Memory Limit:294912/262144 KB (Java/Others)
Total Submissions:32   Accepted:8

[Submit][Status][Discuss]

Description

    IG在S8全球总决赛斩获冠军,王校长很是开心,于是决定多投电竞几个亿,来扩充他的IG军团。他格外重视DotA2分部的扩大。

    在众多DotA2战队中,王校长格外看好的有nn支队。他决定把其中kk支队买下来,来组建一支超级战队。

    每支队都有5名选手,在团队中承担着不同的职责(比如中单、辅助、Carry),可以分别叫作一、二、三、四、五号位。每名选手都有一个能力值vv,代表着他的个人实力。组建超级战队的过程是这样的:对于某号位置,王校长会从已经买下的kk支队中,找出一名能力最强的选手来担任。

    王校长想让这支超级战队的5名选手能力值之和尽量大。现在,让你来选择出kk支战队,从而得到这个最大值。

题解:状压dp[i][j] 表示状态j个人选i的状态的最大值 例如i二进制表示为11001,j为3 即为任意3个队选第1 4 5(从右往左)三种能力的最大值。 转移就是几种能力中选几个能力为一个人贡献 剩下为其他人贡献。状压dp自己看不懂很难讲明白...看代码把

代码:

    #include<bits/stdc++.h>
using namespace std;
int a[10100][5];//原始数据 
int maxx[5];//每种能力最大值 
int dp[1010][5];    //dp 
int n;int k;
int main()
{
    scanf("%d%d",&n,&k);
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<5;j++)
        {
            scanf("%d",&a[i][j]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<5;j++)
        {
            maxx[j]=max(maxx[j],a[i][j]);
        }
        for(int ni=1;ni<(1<<5);ni++)//记录一个人的各个状态下最大值 
		{
            int now=0;
                for(int j=0;j<5;j++)
                {
                    if((ni>>j)&1==1){
                        now+=a[i][j];
                    }
                }
                dp[ni][1]=max(dp[ni][1],now); 
        }
    }
    int ans=0;
    if(k>=5)//超过5个人直接每个能力取最大值 
	{
        for(int j=0;j<5;j++)
        {
            ans+=maxx[j];
        }       
        printf("%d\n",ans);
        return 0;               
	}
    for(int i=1;i<(1<<5);i++)//DP转移 i表示当前状态 
    {
    	for(int j=2;j<=k;j++){
	 	    for(int ni=1;ni<(1<<5);ni++)//ni&i表示一个人占这个状态的最大值 剩下的由其他人提供 
			{
		        if((i&ni)!=0){        
		            dp[i][j]=max(dp[i][j],dp[(ni&i)^i][j-1]+dp[(i&ni)][1]);
		        }
		    }   		
		}
	}
    printf("%d\n",dp[(1<<5)-1][k]);
}

 

Problem G: 光!

Time Limit:3000/1000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)
Total Submissions:12   Accepted:5

[Submit][Status][Discuss]

Description

 

在二维欧几里德空间内有一单位正方形,四顶点分别为(0,0),(0,1),(1,1),(1,0)(如图所示)。在这个单位正方形的四条边上放平面镜,镜面朝向正方形内部。计x轴上的平面镜为a,按照顺时针方向其余三边分别为b、c、d。正方形的重心(0.5,0.5)放有一个点光源(不计体积),某一时刻向右侧射出斜率为有理数p/q的光线,当光线遇到平面镜时会反射,问第t次遇到平面镜时位于哪面平面镜上。

 

题解:将单个格子变成一个无穷大的网格左边第i个格子是原格子i-1次左右翻转,上边同理

那么显然原中心(0.5,0.5)且会经过新中心(0.5+q,0.5+p)。并且经过了p条横线q条竖线。剩下的点暴力枚举判断最后停在的方格数。如果最后一个格是从左边射入第i+1个格子(经过了i条竖线)则i%2==0即为b,i为奇数为d。上下同理。

代码:

#include<bits/stdc++.h>
using namespace std;
	int q,p,T,t;
double gety(double x){
	return x*p/q;
}
double a[400];
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d",&p,&q,&t);
		 int num=t/(p+q);
		 t%=(p+q);
		 if(t==0){
		 	if(p>q){
		 		if((num*p)%2==0){
		 			printf("a\n");continue;
				 }
				 else{
				 	printf("c\n");continue;
				 }
			 }
			 else{
		 		if((num*q)%2==0){
		 			printf("b\n");continue;
				 }
				 else{
				 	printf("d\n");continue;
				 }
			 }
		 }
		 for(int i=1;i<=p;i++){
		 	a[i]=i-0.5;
		 }
		 for(int i=p+1;i<=p+q;i++){
		 	a[i]=gety(i-p-0.5);
		 }
		 //暴力算接下来p+q条线y坐标 
		 sort(a+1,a+1+q+p);//排序求第t%(p+q)个 
		 int pp=0,qq=0;
		 for(int i=1;i<=p+q;i++)
		 {
		 	if(abs(a[i]-0.5-(int)a[i])<=1e-6)//如果是横线 
		 	{
		 		pp++;
			}
			else//如果是竖线 
			{
			 	qq++;
			}
			 if(t==i)//如果已经是第 t%(p+q)个了 
			 {
			  	if(abs(a[i]-0.5-(int)a[i])<=1e-6)
			  	 {
		 		if((num*p+pp)%2==0){
		 			printf("a\n");break;
				 }
				 else{
				 	printf("c\n");break;
				 }
				 }
				 else{
		 		if((num*q+qq)%2==0){
		 			printf("b\n");break;
				 }
				 else{
				 	printf("d\n");break;
				 }
				 }	
			 } 
		}
	}
}

Problem H: 暖气管道

Time Limit:4000/2000 MS (Java/Others)   Memory Limit:294912/262144 KB (Java/Others)
Total Submissions:9   Accepted:4

[Submit][Status][Discuss]

Description

    几年前,大工还有独立的供暖系统。那时候,无论是在宿舍还是教学楼,人常常热得汗流夹背,只好打开窗户。后来,为了响应推进生态文明建设的号召,我们学校决定进行供暖改造,并入大连市内的供暖网络,撤除原有大功耗的供暖锅炉。

    整个大工有nn个供暖区,标号1...n1...n。又有mm条供暖管道,每条管道连接uu和vv两个供暖区。每个供暖区的面积是SiSi。

    改造的计划是这样的:首先,要保证宿舍区的供暖质量。假设宿舍区的标号是kk。现在要去掉一条供暖管道,使得宿舍区所处的联通块缩小,从而使其总面积变小。联通块的面积越小,供暖的质量越佳。(关于联通块的概念,请百度一下)

    所以,请你求出:去掉一条供暖管道后,宿舍区所处联通块的最小总面积。

 

题解:双联通分量缩点 就变成求树上权值最大的支路了,可能会补吧。

1249: Not XOR but OR

Time Limit:8000/6000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)
Total Submissions:12   Accepted:5

[Submit][Status][Discuss]

Description

给你一个包含 n 个正整数序列A和一个正整数 k. 你的目标是将这 n 个正整数划分成 k 个不相交连续非空的子序列, 以至于每个元素都恰好属于一个子序列. 每个子序列可以有两个整数 l 和 r (l≤r) 描述, 其含义是A[l],A[l+1],…,A[r]. 这样的一个子序列的价值是所有元素的按位或, 即A[l]|A[l+1]|…|A[r] . 总的价值是所有的子序列的价值和. 现在你必须找道一种划分方式使得总的价值和最大.

如果你的不知道按位或的话, 你能了解更多关于[按位或](https://en.wikipedia.org/wiki/Bitwise_operation#OR).

Input

第一行包括一个正整数 T​, 表示测试数据的数目.

接下来 T 组测试数据如下: 第一行包括每组测试数据的两个正整数 nn​ 和 kk​, 表示序列元素的数目和要求划分的子序列数目.

第二行包括 n 个正整数, 表示该序列.

Output

对于每组测试数据, 输出一行, 包含所能达到的最大价值.

Sample Input

4
3 2
1 2 2
4 3
1 2 3 4
2 2 
1 2
11 4
66 152 7 89 42 28 222 69 10 54 99

Sample Output

5
10
3
704

HINT

●1≤T≤10

●1≤n≤1000

●1≤k≤n

●1≤A[i]≤2^30

●保证所有组的 n 的和不超过 5000

Source

Janaldo

按何大佬的要求补一下题解ORZ

提示:n只有1000 而且复杂度是O(n^2logn),当时没看范围以为是神仙题就一直没碰,结果发现n范围1000......

 

题解:设原数组为A数组。很容易想到dp[i][j]表示前i个数分成k组的最大值 ,那么问题在于怎么进行状态转移。

显然dp[i][j]=max(dp[t][j-1]+(A[t+1]|A[t+2]......|A[i]))。但这样转移时O(n)的,总复杂度就变成了n^3。

接着我们可以发现随着dp[i][j]会随着i的值变大(即dp[i][j]>=dp[i-1][j])而(A[t+1]|A[t+2]......|A[i])的值只会变换不超过30次。所以我们取30个变换点进行DP复杂度就变成O(31n^2)了。

代码:

#include<bits/stdc++.h>

using namespace std;

long long dp[1020][1020];//dp[i][j]表示前i个数分成k组的最大值 
int a[1020];//原数组 
int pos[32];//pos[i]为第i位上次出现的标 
int tmp[2][32];
map<int,int> ma;//--------map的key值为出现的下标,value为当前位或运算到那一位的值 
int main()
{
	int T;
	scanf("%d",&T);map<int,int>::iterator iter;
		int tt=(1<<30)-1;tt+=(1<<30);
	while(T--){
		int n,k;
		scanf("%d%d",&n,&k);
		//---------------初始化 
		ma.clear();
		for(int i=1;i<=n;i++){ 
			scanf("%d",&a[i]);
			for(int j=0;j<=k;j++){
				dp[i][j]=0;
			}
		}
		for(int i=0;i<30;i++){
			pos[i]=0;
		}	
		//---------------------
		for(int i=1;i<=n;i++){
			for(int j=0;j<30;j++){
				if((a[i]>>j)&1){
					if(pos[j]!=0){//-------如果map里有这个数就减掉相应的数 
						ma[pos[j]]&=((1<<j)^tt);
						if(ma[pos[j]]==0){
							ma.erase(pos[j]);	
						}
					}
					pos[j]=i;
					if(ma.count(pos[j])){
					
						ma[pos[j]]=ma[pos[j]]|(1<<j);
					}
					else{
						ma[pos[j]]=(1<<j);
					}					
				}
			}			
				    int ti=0;//--------------因为map默认是按递增排序的,所以这里存进数组倒着用。 
				    for(iter=ma.begin(); iter!=ma.end(); iter++)
				    {ti++;
				    	tmp[1][ti]=iter->second;
				    	tmp[0][ti]=iter->first;	
					}//------------------------------------------------------------------------ 
			dp[i][1]=dp[i-1][1]|a[i];
			for(int j=2;j<=k;j++){
				if(j>i) break;
				int now=a[i];
			    for(int d=ti; d>=1;d--)
			    {
			    	if(tmp[0][d]-1<j-1)
					{
						break;
					}
			    	now|=tmp[1][d];
			    	dp[i][j]=max(dp[i][j],dp[tmp[0][d]-1][j-1]+now);//------------------------------dp转移关系 
				}
			}
		}
		printf("%lld\n",dp[n][k]);
	}
 } 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值