2015多校联合训练总结(2)

本文总结了ACM竞赛中的解题经验,包括线段树、动态规划、图论等问题的解决方法,并提供了详细的题解及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


相比较第一场,少了些许紧张,但是同样暴露了我们三人配合上的问题,开始被大神一句:第七道是一道水线段树,于是便直奔去搞线段树,结果这个题是过的人数最少的题之一~然后我们三人一人读一道题,甚至开始自己鼓捣怎么写,于是很长时间大家都在安静的搞自己觉得可能会搞出来的题,于是乎很长时间,都没有结果,然后大家慢慢弃疗,开始专注的考虑一道题,最终ac了一道。所以在慢慢的训练中我们才会共同成长吧~~

下面是其中某些题的题解

A:http://acm.hdu.edu.cn/showproblem.php?pid=5300

题很长,过的人很少很少,放弃

B:http://acm.hdu.edu.cn/showproblem.php?pid=5301

题意:n*m的矩阵里面有一个1*1的小黑块,用大小不同的小矩形铺满n*m的矩形,不能覆盖小黑块,只能相接,同时这些小矩形必须满足至少有一边在大矩形的边框上,使得这些小矩形都尽量小,使得面积最大的小矩形面积最小,这个小矩形面积多大

思路:读题费了很长时间,“minimize the maximum areas of all apartments”,在图文并茂下终于读懂了,第一感觉就是一个机智题,大致找了一规律。就是最短边加1的一半,如果在黑块正好在正方形的正中间,那么就减一,预料中的wa了、、然后就开始考虑特殊情况,那就是类似于这种的

按照最初的思路,结果应该是2,但是明显的,不符合条件,所以我又用了另一种方法,求小黑块四个方向距离四边的最小值,并取四个最小值中的最大值,然后用这两种结果取最大值,就是结果,最终ac了

过程中还卡了很久,原因竟然是n和m弄反了。。。。TnT

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cctype>
#include <numeric>
#include <iomanip>
#include <bitset>
#include <sstream>
#include <fstream>
#define debug "output for debug\n"
#define pi (acos(-1.0))
#define eps (1e-8)
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 1000005;
int main()
{
    ll n,m,x,y;
    ll a[10];
    while(scanf("%lld %lld %lld %lld",&n,&m,&x,&y)!=EOF)
    {
        if(n==1&&m==1)
        {
            printf("0\n");
            continue;
        }
     if(n==1||m==1||n==2||m==2)
     {
         printf("1\n");
        continue;
     }
    a[0]=min(y-1,x);
    a[0]=min(a[0],n-x+1);
    //cout<<a[0]<<endl;
    a[1]=min(x-1,y);
    a[1]=min(m-y+1,a[1]);
    //cout<<a[1]<<endl;
    a[2]=min(x,m-y);
    a[2]=min(a[2],n-x+1);
    //cout<<a[2]<<endl;
    a[3]=min(y,n-x);
    a[3]=min(a[3],m-y+1);
    //cout<<a[3]<<endl;
    ll maxn=max(max(max(a[0],a[1]),a[2]),a[3]);
    ll sum=(min(n,m)+1)/2;
    if(n==m&&n%2&&x==(n+1)/2&&y==(n+1)/2)sum--;

    printf("%lld\n",max(sum,maxn));
    }
    return 0;
}

 

C:http://acm.hdu.edu.cn/showproblem.php?pid=5302 

题意:给你六个点,分别是白点度为0的有a1个,白点度为1的有a2个,白点度为2的点有a3个,黑色的度为0的点b1个,黑点度为1的点有b2个,黑色度为2的点有b3个,同时要求白点个数等于黑点个数,要求构造一个图满足这个条件

思路:若a2&1 或b2&1 则无解。度为1的点必须成对存在,在这只有0 1 2度数的图中。白色边和黑色边不相互影响,他们是独立的。总的边数为总度数/2 。即:(a2+a3*2)/2.

先构造白边度数为2的点,必然是(1,2)(2,3)(3,4)....(a3+1,a3+2)..如此下去,直到完成度数2的节点的构造。

再够着白边度数为1的点,(a3+3,a3+4) (a3+5,a3+6).......

最后白色边度数为0的点,(,)(,)...(a1+a2+a3,a1+a2+a3)

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int T;
int a[5],b[5];
int d[1000005];
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        for(int i = 0; i<3; i++)
            scanf("%d",&a[i]);
        for(int i = 0; i<3; i++)
            scanf("%d",&b[i]);
        int sum = 0;
        for(int i = 0; i<3; i++)
            sum+=a[i];
        if( (a[1]&1) || (b[1]&1) )
        {
            puts("-1");
            continue;
        }
        if(sum==4)
        {
            puts("4\n1 2 0\n1 3 0\n2 3 1\n3 4 1");
            continue;
        }
        printf("%d\n",a[1]/2+a[2]+b[1]/2+b[2]);
        int t = 1;
        while(a[2]!=-1)
        {
            printf("%d %d 0\n",t,t+1);
            t++;
            a[2]--;
        }
        t++;
        while(a[1]!=2)
        {
            printf("%d %d 0\n",t,t+1);
            t+=2;
            a[1]-=2;
        }

        int tt = 0;
        for(int i = 1; i<=sum; i+=2)
            d[tt++] = i;
        for(int i = 2; i<=sum; i+=2)
            d[tt++] = i;

        t = 0;
        while(b[2]!=-1)
        {
            printf("%d %d 1\n",min(d[t],d[t+1]),max(d[t],d[t+1]));
            t++;
            b[2]--;
        }
        t++;
        while(b[1]!=2)
        {
            printf("%d %d 1\n",min(d[t],d[t+1]),max(d[t],d[t+1]));
            t+=2;
            b[1]-=2;
        }

    }
}

D:http://acm.hdu.edu.cn/showproblem.php?pid=5303

题意:一个圆形环道上有n棵苹果树,从顺时针方向看,每个苹果树分别上距离原点分别有di米的位置,有ai个苹果,每次最多能拿k个苹果,若要把所有苹果都摘到原点,问最少需要走多少米

思路:如果道路不是环形的,那么就把道路平均截成两段贪心即可,但是这样有一个问题,就是有时候可能需要走整个环,但是可能想到的是这个环最多只会走1次,因为如果走两次的话,可以分别走两个半环, 接下来的处理两种思路,一种是左右两侧分别以每k个一组,剩下的分别采取1.走一圈,左边剩余的单独一回,2.走一圈,右边剩余的单独一回,3.左侧的单独走,右侧的单独走,三种方式找最小的。第二种是记录摘取每个苹果所需要的累计的路程,然后把最后一个作为所用路程的一半,暴力从左侧都包含,逐渐右移,再到全右侧,找了最小的。

代码:(第二种方法)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cstdlib>
using namespace std;
typedef long long ll;
int x[2111111];int n;
int l,k;

int a1[2111111],n1,a2[2111111],n2;
ll ans=0;
ll su1[2111111],su2[2111111];
int main()
{
	int tes;
	scanf("%d",&tes);
	while(tes--){
		int cnt;
		scanf("%d%d%d",&l,&cnt,&k);
		n=0;
		while(cnt--){
			int y,a;
			scanf("%d%d",&y,&a);
			for (int i=1;i<=a;i++)x[++n]=y;
		}
		k=min(k,n);
		n1=n2=0;
		for (int i=1;i<=n;i++)
			if(x[i]*2<l)a1[++n1]=x[i];
			else a2[++n2]=l-x[i];
		sort(a1+1,a1+1+n1);
		sort(a2+1,a2+1+n2);
		su1[0]=su2[0]=0;
		for (int i=1;i<=n1;i++)
			{
			    if(i<=k)su1[i]=a1[i];
			else su1[i]=a1[i]+su1[i-k];
			//cout<<su1[i]<<endl;
			}
		for (int i=1;i<=n2;i++)
			{
			    if(i<=k)su2[i]=a2[i];
			else su2[i]=a2[i]+su2[i-k];//取第i个苹果所需要走的步数,su2[i-k]上一回取得时候所走的步数
			//cout<<su2[i]<<endl;
			}
		ans=(su1[n1]+su2[n2])*2;
		//cout<<ans<<endl;
		for (int i=0;i<=n1 && i<=k;i++){//暴力从a2搜到了两侧都包含,再到全a1,找了最小的
			int lef=n1-i,rig=max(0,n2-(k-i));
			ans=min(ans,l+(su1[lef]+su2[rig])*2);
		}
		printf("%lld\n",ans);

	}
	return 0;
}








 

E:状压dp

F:http://acm.hdu.edu.cn/showproblem.php?pid=5305

题意:给你n个人,这n个人构成m对朋友,现在想要这n个人每个人都拥有相等的线上朋友和线下朋友,问能不能实现,如果不能输出0,否则输出有几种方式

思路:dfs搜索,分别假设第i对朋友是线上朋友,要努力保证线上朋友和线下朋友相等,以此类推,直到最后一个,如果这m对都满足,那么sum++,最后的sum就是所要求的结果

代码:

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <iomanip>
#include <queue>
#include <ctime>
#include <vector>
#include <ctype.h>
#define maxn 1005
#define ll long long
#define INF 999999999
using namespace std;

struct Edge
{
    int a,b;
}e[35];

int n,m,f[10],up[10],dw[10];
int ans;

void dfs(int u)
{
    if(u==m)
    {
        ans++;
        return;
    }
    int a,b;
    a=e[u].a;
    b=e[u].b;
    //cout<<a<<" "<<b<<endl;
    f[a]--;
    f[b]--;
    up[a]++;
    up[b]++;
    if((f[a]&&f[b])||(!f[a]&& f[b] && up[a]==dw[a])||(!f[b] && f[a] && up[b]==dw[b])||(!f[a]&& !f[b] && up[a]==dw[a] && up[b]==dw[b]))
    {
        dfs(u+1);
    }
    up[a]--;
    up[b]--;
    dw[a]++;
    dw[b]++;
    if((f[a]&&f[b])||(!f[a] && f[b] && up[a]==dw[a])||(!f[b]&& f[a] && up[b]==dw[b])||(!f[a]&& !f[b]&& up[a]==dw[a] && up[b]==dw[b]))
    {
        dfs(u+1);
    }
    dw[a]--;
    dw[b]--;
    f[a]++;
    f[b]++;
}

int main()
{
    int t,k;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        memset(f,0,sizeof (f));
        memset(up,0,sizeof(up));
        memset(dw,0,sizeof(dw));
        ans=0,k=1;
        for(int i=0;i<m;i++)
        {
            cin>>e[i].a>>e[i].b;
            f[e[i].a]++;
            f[e[i].b]++;
        }
        for(int i=1;i<=n;i++)
        {
            if(f[i]%2!=0)
            {
                k=0;
                break;
            }
        }
        if(k==0)
        {
            cout<<0<<endl;
        }
        else
        {
            dfs(0);
            cout<<ans<<endl;
        }
    }
    return 0;
}


G:暴力线段树,开头听了大神言,小伙伴写了半个多小时,无结果,弃疗、、

H:FFT快速傅氏变换,复变中的傅氏变换都没学明白,对于FFT代码、、

I:http://acm.hdu.edu.cn/showproblem.php?pid=5308

题意:用n个n用+、-、*、/、()这几个符号得到24点

思路:暴力找规律,直接输出

代码:

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
using namespace std;
int n;
int main(){
//	freopen("data.in","r",stdin);
//	freopen("dataa.out","w",stdout);
	while (scanf("%d",&n)!=EOF){
		if (n>=15){
			printf("1 + 2\n"); printf("3 + 4\n"); printf("5 + 6\n"); printf("7 + 8\n"); printf("%d + 9\n",n+1);
			printf("%d / 10\n",n+2); printf("%d / 11\n",n+3); printf("%d / 12\n",n+4); printf("%d / 13\n",n+5);
			printf("%d * %d\n",n+6,n+7); printf("%d * %d\n",n+8,n+9); printf("%d * %d\n",n+10,n+11); printf("14 - 15\n");
			int now=n+13; for (int i=16;i<=n;i++){printf("%d * %d\n",i,now); now++;} printf("%d + %d\n",n+12,now); continue;
		}
		if (n>=12){
			printf("1 + 2\n");
			for (int i=1;i<=n*2-24;i++) printf("%d / %d\n",i*2+1,i*2+2); int num=n*4-46;
			int now=n+1+n*2-24;
			if (n!=12){
				printf("%d - %d\n",n+1,n+2); now++;
				for (int i=2;i<=n*2-24;i++){printf("%d - %d\n",now,n+1+i); now++;} 
			}
			int k1=now;
			printf("%d - %d\n",num+1,num+2); num+=3; now++;
			for (int i=num;i<=n;i++){printf("%d * %d\n",i,now); now++;}
			printf("%d + %d\n",k1,now); continue;
		}
		if (n>=10){
			printf("1 + 2\n");
			for (int i=1;i<=24-n*2;i++) printf("%d / %d\n",1+2*i,2+2*i);
			printf("%d + %d\n",n+1,n+2); int now=n+26-n*2;
			for (int i=2;i<=24-n*2;i++) {printf("%d + %d\n",n+1+i,now); now++;}
			if (n==10) continue;
			printf("7 - 8\n"); printf("17 * 9\n"); printf("18 * 10\n"); printf("19 * 11\n"); printf("20 + 16\n"); continue;
		}
		if (n==9){
			printf("1 + 2\n"); printf("3 + 10\n"); printf("4 / 5\n"); printf("6 / 7\n"); printf("8 / 9\n");
			printf("11 - 12\n"); printf("15 - 13\n"); printf("16 - 14\n"); continue;
		}
		if (n==8){
			printf("1 + 2\n"); printf("3 + 9\n"); printf("4 - 5\n"); printf("6 * 11\n"); printf("7 * 12\n");
			printf("8 * 13\n"); printf("10 + 14\n"); continue;
		}
		if (n==7){
			printf("1 * 2\n"); printf("3 / 4\n"); printf("5 + 6\n"); printf("8 - 9\n"); printf("11 / 10\n"); printf("12 * 7\n"); continue;
		}
		if (n<=3){printf("-1\n"); continue;}
		if (n==6){
			printf("1 * 2\n"); printf("7 - 3\n"); printf("8 - 4\n"); printf("9 - 5\n"); printf("10 + 6\n"); continue;
		}
		if (n==5){
			printf("1 / 2\n"); printf("6 / 3\n"); printf("4 - 7\n"); printf("5 * 8\n"); continue;
		}
		if (n==4){
			printf("1 * 2\n"); printf("3 + 5\n"); printf("4 + 6\n"); continue;
		}
	}
	return 0;
}

J:DP弃疗~
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值