2019.3.6 基础算法训练(周三场)

A.费解的开关
题意:
给1个5*5的灯矩阵 0表示灯暗 1表示灯亮
若改变其中一个灯的状态 其四周的灯的状态也会变化
问能否在6步之内将所有灯打开。
题解:
跟我之前写的poj3279题目很相似 poj3279
多用1个cnt记录一下步数 大于6时剪枝即可
代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn = 1e5+7;
const int inf = 1e9+7;
typedef long long LL;
int T;
char s[10];
int m[10][10],num[10][10],cnt;
int ans;

bool check(int x,int y)
{
    int temp=m[x][y];
    temp+=num[x-1][y]+num[x][y]+num[x][y-1]+num[x][y+1];
    return temp%2;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        ans=inf;
        for(int i=1;i<=5;i++)
        {
            scanf("%s",s+1);
            for(int j=1;j<=5;j++)
                m[i][j]=s[j]-'0';
        }
        for(int i=0;i<(1<<5);i++)
        {
            memset(num,0,sizeof(num));
            cnt=0;
            for(int j=0;j<5;j++)
            {
                num[1][5-j]=(i>>j)&1;
                cnt+=(i>>j)&1;
            }
            for(int j=2;j<=5 && cnt<=6;j++)
            {
                for(int k=1;k<=5 && cnt<=6;k++)
                {
                    if(!check(j-1,k))
                    {
                        cnt++;
                        num[j][k]=1;
                    }
                }
            }
            if(cnt<=6)
            {
                int ok=1;
                for(int j=1;j<=5;j++)
                    if(!check(5,j))
                    {
                        ok=0;
                        break;
                    }
                if(ok)
                    ans=min(ans,cnt);
            }

        }
        if(ans==inf)
            printf("-1\n");
        else printf("%d\n",ans);
    }
	return 0;
}

B.Ultra-QuickSort
题意:
给一个长度为N的数组a[],问a[]中有多少对逆序对?
题解:
逆序对:递减的2个数组成的数对(A[i]>A[j] && i<j),例如在{3,2,4,0}中逆序对有(3,2)(3,0)(2,0)(4,0)4对
计算方法有许多种:
1.冒泡排序。冒泡排序的过程其实就是发现有逆序对就交换2个数的位置,所以只要在排序过程中计数即可。但由于这道题的数据是5e5,O(n^2)会超时。
2.线段树(树状数组)。O(nlogn)。
3.归并排序。O(nlogn)。
2和3用的原理都是一样的
假设数组里的数字前k个是非递减的,第k+1个比前k个中的x个小,那么添加第k+1个数字时逆序对就增加了x个
我用的是归并排序,具体就不讲了,以后有时间再看看吧。。。数据爆int要用LL

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
const int maxn = 5e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int a[maxn],temp[maxn],cnt;
LL ans;

void Merge(int l,int m,int r)
{
    int p=l,q=m+1;
    cnt=0;
    while(p<=m && q<=r)
    {
        if(a[p]<=a[q])
            temp[cnt++]=a[p++];
        else
        {
            ans+=m+1-p;
            temp[cnt++]=a[q++];
        }
    }
    while(p<=m) temp[cnt++]=a[p++];
    while(q<=r) temp[cnt++]=a[q++];
    for(int i=0;i<cnt;i++)
        a[i+l]=temp[i];
}

void Sort(int l,int r)
{
    if(l==r)
        return;
    int m=(l+r)/2;
    Sort(l,m);
    Sort(m+1,r);
    Merge(l,m,r);
}
int main()
{
    while(scanf("%d",&n)!=EOF && n)
    {
        ans=0;
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        Sort(0,n-1);
        //for(int i=0;i<n;i++)
            //printf("%d ",a[i]);
        printf("%lld\n",ans);
    }

	return 0;
}

C.货仓选址
题意:
有N个工厂,给出N个工厂的横坐标X[i],要建立一个货仓给N个工厂配送东西,问建在哪里可以使货仓到各个工厂的路程之和最小?
题解:
贪心,显然看到这道题就把坐标按从小到大排序,然后发现建在中位数时路程之和最小。
代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn = 1e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int a[maxn];
double k,ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    if(n%2)
        k=a[n/2+1];
    else
        k=(a[n/2]+a[n/2+1])/2;
    for(int i=1;i<=n;i++)
        ans+=fabs(a[i]-k);
    printf("%.0f\n",ans);
	return 0;
}

D.Sunscreen
题意:
C头奶牛,每头奶牛都要去晒太阳,晒太阳前要擦防晒霜,每头奶牛能接受的SPF的范围是[ Min[i],Max[i] ],有L种防晒霜,给出SPF和瓶数,这里我用l[i]表示SPF为i的防晒霜的瓶数。每头奶牛只能涂一瓶防晒霜,问最多有几头奶牛可以获得防晒?
题解:
贪心。
1.将Min[i]按递减排序。然后对于每一头奶牛,从Max[i]~Min[i]搜索防晒霜,存在的话就给这头奶牛。
2.将Max[i]按递增排序。然后对于每一头奶牛,从Min[i]~Max[i]搜索防晒霜,存在的话就给这头奶牛。
证明1(2同理):
若按上述排序排好,对于任意上下2头牛(黑色和红色)有2种情况
1.红色的Max小于黑色的Max
2.红色的Max大于等于黑色的Max
对于黑色的任意2瓶可选的防晒霜(假设SPFx<SPFy)(如果只有1瓶那就给黑色的涂)
对于第一种情况红色的牛有3种情况,x和y都可以涂,x可以涂y不能涂,x和y都不能涂。
如果是(+,+),那么1人1瓶。
如果是(+,-),x给红色 y给黑色
如果是(-,-),黑色从x和y中任选1瓶
对于第二种情况跟(+,+)同理。
所以综合起来就是优先将SPF大的给前面的牛
也可以反过来,所有有2种贪心方式
1
代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
const int maxn = 2500+7;
const int inf = 1e9+7;
typedef long long LL;
int C,L,ans;
struct Cow
{
    int Min,Max;
    bool operator < (const Cow a) const
    {
        if(Min==a.Min)
            return Max>a.Max;
        return Min>a.Min;
    }
};
Cow c[maxn];
int l[1007];
int main()
{
    scanf("%d %d",&C,&L);
    for(int i=1;i<=C;i++)
        scanf("%d %d",&c[i].Min,&c[i].Max);
    for(int i=1;i<=L;i++)
    {
        int SPF,num;
        scanf("%d %d",&SPF,&num);
        l[SPF]+=num;
    }
    sort(c+1,c+1+C);
    for(int i=1;i<=C;i++)
    {
        for(int j=c[i].Max;j>=c[i].Min;j--)
            if(l[j])
            {
                l[j]--;
                ans++;
                break;
            }
    }
    printf("%d\n",ans);
	return 0;
}

E.递归实现排列型枚举
题意:
给你一个N,要求输出N的全排列数字串(1~N),按字典序从小到大排序
例如N=3
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
题解:
dfs+回溯
代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
const int maxn = 1e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int vis[15],ans[15],cnt;

void dfs(int num)
{
    if(num==n+1)
    {
        for(int i=0;i<cnt;i++)
            printf("%d ",ans[i]);
        printf("\n");
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            vis[i]=1;
            ans[cnt++]=i;
            dfs(num+1);
            cnt--;
            vis[i]=0;
        }
    }
}
int main()
{
    scanf("%d",&n);
    dfs(1);
	return 0;
}

F.七夕祭
G.奇数码问题
题意:
给一个奇数N(N<500),给出2个N*N的棋盘A、B(0~N*N-1,0是空格)
问A能否通过移动到达B?
题解:
考察A、B的逆序对个数即可
奇数码在移动过程中整个棋盘的逆序对奇偶性不变
所以判断A、B的逆序对个数奇偶性是否相同
代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <vector>
using namespace std;
const int maxn = 3e5+7;
const int inf = 1e9+7;
typedef long long LL;
int n;
int a[maxn],temp[maxn],cnt;
LL ans;
void Merge(int l,int m,int r)
{
    int p=l,q=m+1;
    cnt=0;
    while(p<=m && q<=r)
    {
        if(a[p]<=a[q])
            temp[cnt++]=a[p++];
        else
        {
            ans+=m+1-p;
            temp[cnt++]=a[q++];
        }
    }
    while(p<=m) temp[cnt++]=a[p++];
    while(q<=r) temp[cnt++]=a[q++];
    for(int i=0;i<cnt;i++)
        a[i+l]=temp[i];
}
void Sort(int l,int r)
{
    if(l==r)
        return;
    int m=(l+r)/2;
    Sort(l,m);
    Sort(m+1,r);
    Merge(l,m,r);
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        ans=0;
        int num,len=0;
        for(int i=0;i<n*n;i++)
        {
            scanf("%d",&num);
            if(num)
                a[++len]=num;
        }
        Sort(0,len);
        len=0;
        for(int i=0;i<n*n;i++)
        {
            scanf("%d",&num);
            if(num)
                a[++len]=num;
        }
        Sort(0,len);
        if((ans)%2)
            printf("NIE\n");
        else printf("TAK\n");
    }

	return 0;
}

H.Genius ACM
I.Sumdiv
J.最短Hamilton路径

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值