ACM-ICPC 2017南宁赛区网络赛题目题解收集

前言:本篇文章的主要代码来自于CSDN博客或者网络,感谢网络ACMdalao的AC代码,佩服他们,本文最后会写出各个AC代码的原作者和网址。(注意一点,每个程序的头文件我都改成了万能头文件,测试时间可能会加长,如果需要源代码的头文件请直接在代码后面的链接里面去找一下,我喜欢C++里面不用类似于stdio.h的头文件写法的头文件,我可能会写成cstdio,注意,万能头文件在POJ里面是不能识别的,会出现CE,这个时候你就需要修改万能头文件成你所需要的头文件)
ACM-ICPC 2017南宁赛区网络赛题目及其题解
题目列表

  • A.Weather Patterns
  • B.Train Seats Reservation
  • C.Auction Bidding
  • D.Path Search with Constraints
  • E.Visible Surface
  • F.Overlapping Rectangles
  • G.Finding the Radius for an Inserted Circle
  • H.A Cache Simulator
  • I.GSM Base Station Identification
  • J.Minimun Distance in a Star Graph
  • K.Line Segments Clipped by Windows
  • L.The Heaviest Non-decreasing Subsequence Problem
  • M.Frequent Subsets Problem
    下面开始单个题目的题目内容和解析代码。
    A.Weather Patterns(数学期望+英语阅读理解)
    题目链接:https://nanti.jisuanke.com/t/17308
    PS:看了之后感觉,学好英语很重要,这道题主要考察ACMer的英语阅读理解能力,题目本身是不难,但是英语理解需要一点能力。真的当时被坑了。
    题意:这题一点都不难,一道英语阅读理解题,读懂了你就会做了!这道题其实就是每天会有四种天气,分别是①下雪②下雨③多云④晴天,然后给你一个4*4的矩阵,这个矩阵的第i行第j个表示的就是从天气i到天气j的概率,然后接下来给你两个观察序列,问你天气完全按照这个观察序列上的顺序变化的概率是多少,第一天是任何天气的概率都为1。接下来再给你两个数i和j,问连续数天都是i天气和j天气的数学期望分别是多少。
    思路:对于任意一个观察序列,我们只要按序列的顺序把两个天气之间变化的概率不断地乘就可以得出答案,比如说题目中的第二个序列,2 1 1 1 3 3 4,那么a[2][1]*a[1][1]*a[1][1]*a[1][3]*a[3][3]*a[3][4]就是答案。对于数学期望,我们就是求连续1天是i天气的概率+连续2天是i天气的概率+……+连续n天是i天气的概率,虽然这个n是无限大的,但是当加到后面之后这个概率会非常小可以忽视,所以我们只需要加1000个差不多就够了。
    代码如下:
#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 1e9 + 5;
const int MAXN = 100000007;
const int MOD = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1.0);
LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a%b); }


double MA[5][5];

int main()
{
    double ans, res;
    int x, y;
    char ch;
    for (int i = 1; i <= 4; i++)
        for (int j = 1; j <= 4; j++)
            scanf("%lf", &MA[i][j]);
    ans = 1;
    getchar();
    y = -1;
    while (scanf("%d", &x))
    {
        if (y == -1)
        {
            y = x;
            continue;
        }
        else
        {
            ans *= MA[y][x];
            y = x;
        }
        ch = getchar();
        if (ch != ' ')
            break;
    }
    printf("%.8f\n", ans);
    y = -1;
    ans = 1;
    while (scanf("%d", &x))
    {
        if (y == -1)
        {
            y = x;
            continue;
        }
        else
        {
            ans *= MA[y][x];
            y = x;
        }
        ch = getchar();
        if (ch != ' ')
            break;
    }
    printf("%.8f\n", ans);
    scanf("%d", &x);
    ans = 0;
    res = 1;
    for (int i = 1; i <= 1000; i++)
    {
        ans += res;
        res *= MA[x][x];
    }
    printf("%.8f\n", ans);
    scanf("%d", &x);
    ans = 0;
    res = 1;
    for (int i = 1; i <= 1000; i++)
    {
        ans += res;
        res *= MA[x][x];
    }
    printf("%.8f\n", ans);
}

本题感谢Effiel大佬的AC代码,附上原作者题解地址:
http://blog.csdn.net/kele52he/article/details/78078507

B.Train Seats Reservation(暴力遍历)
题目链接:https://nanti.jisuanke.com/t/17309
题意:一辆火车从1开到n,给你几个从l上车,r下车的人数。让你求这辆火车至少要装多少座位才可以都坐下。
POINT:
数据很小,暴力,求区间1到n里同时人数最多的就行。注意r这个点的人是要下车的,可以同时上车。
代码如下:

#include 
using namespace std;
#define LL long long
const LL maxn = 111;
LL a[maxn];
int main()
{
    LL n;
    while(~scanf("%lld",&n))
    {
        if(n==0)
        {
            printf("*\n");
            break;
        }
        memset(a,0,sizeof a);
        LL ans=0;
        LL l,r,k;
        for(LL j=1;j<=n;j++){
        scanf("%lld %lld %lld",&l,&r,&k);
            if(l>r) swap(l, r);
        for(LL i=l;iif(ansprintf("%lld\n",ans);

    }
}

感谢Mr_Treeeee的AC代码,下面给出原代码链接:
http://blog.csdn.net/mr_treeeee/article/details/78078435

C.Auction Bidding
题目链接:https://nanti.jisuanke.com/t/17310
本题暂时没找到AC代码,待补
D.Path Search with Constraints
题目链接:https://nanti.jisuanke.com/t/17311
本题暂时没找到AC代码,待补
E.Visible Surface
题目链接:https://nanti.jisuanke.com/t/17312
本题暂时没找到AC代码,待补
F.Overlapping Rectangles(离散+线段树,需要离散数学和数据结构的知识)
题目链接:https://nanti.jisuanke.com/t/17313
题意:计算矩形面积并
思路:主要用到了离散数学+线段树的知识,有模板的可以直接套用模板,然后修改一下就能过
PS:这道题我们队原来就没想着过,结果我们队的一个dalao直接用模板就AC了O(∩_∩)O哈哈~,当时就吃惊了,因为那个时候我们在死磕B(说句实话,我们的B花的时间太多了T^T,主要是想复杂了)
附上原作者的AC代码:

#include
using namespace std;
#define LL long long
const LL maxn = 1111;
#define lt x<<1
#define rt x<<1|1
LL a[maxn];
struct len
{
    int l,r;
    int kind;
    int h;
}l[maxn*2];
struct ju
{
    int a,b,c,d;
}z[maxn];
int p[maxn*2],num;
int pp[maxn*2];
int sum[maxn*8];
int add[maxn*8];
bool cmd(len a,len b)
{
 //   if(a.h!=b.h)
    return a.hvoid pushdown(int x,int l,int r)
{
    sum[lt]+=add[x];
    add[lt]+=add[x];
    sum[rt]+=add[x];
    add[rt]+=add[x];
    add[x]=0;
}
void change(int x,int l,int r,int ll,int rr,int kind)
{
    if(add[x]!=0)
    {
        pushdown(x,l,r);
    }
    if(ll<=l&&rr>=r)
    {
        add[x]=kind;
        sum[x]+=kind;
    }
    else
    {
        int mid=(l+r)>>1;
        if(ll<=mid)
        {
            change(lt,l,mid,ll,rr,kind);
        }
        if(mid1,r,ll,rr,kind);
        }
    }
}
int query(int x,int l,int r)
{
    if(add[x]) pushdown(x, l, r);
    int ans=0;
    if(l==r)
    {
        if(r==num) return 0;
        if(sum[x]>0)
            ans+=p[r+1]-p[l];
    }
    else
    {
        int mid=(l+r)>>1;
        ans+=query(lt,l,mid);
        ans+=query(rt,mid+1,r);
    }
    return ans;

}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n==0)
        {
            printf("*");
            break;
        }
        num=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d %d %d",&z[i].a,&z[i].b,&z[i].c,&z[i].d);
            pp[++num]=z[i].a;
            pp[++num]=z[i].c;
        }
        sort(pp+1,pp+1+num);
        for(int i=1;i<=num;i++)
        {
            p[i]=pp[i];
        }
        int lennum=0;
        for(int i=1;i<=n;i++)
        {
            l[++lennum].l=lower_bound(pp+1,pp+1+num,z[i].a)-pp;
            l[lennum].r=lower_bound(pp+1,pp+1+num,z[i].c)-pp;
            l[lennum].kind=1;
            l[lennum].h=z[i].b;
            l[++lennum].l=lower_bound(pp+1,pp+1+num,z[i].a)-pp;
            l[lennum].r=lower_bound(pp+1,pp+1+num,z[i].c)-pp;
            l[lennum].kind=-1;
            l[lennum].h=z[i].d;
        }
        sort(l+1,l+1+lennum,cmd);
        memset(sum,0,sizeof sum);
        memset(add,0,sizeof add);
        int ans=0;
        for(int i=1;i<=lennum;i++)
        {
            int temp=query(1, 1, num);
            if(i>=2)
            {
                ans+=temp*(l[i].h-l[i-1].h);
            }
            change(1,1,num,l[i].l,l[i].r-1,l[i].kind);

        }
        printf("%d\n",ans);


    }
}

本题AC代码提供者还是Mr_Treeeee,感谢Mr_Treeeee的AC代码,下面给出原代码链接:
http://blog.csdn.net/mr_treeeee/article/details/78078435

G.Finding the Radius for an Inserted Circle(运算几何,二分)
题目链接:https://nanti.jisuanke.com/t/17314
这道题需要数学知识,具体看下面。
题意:这道题就是给你三个一模一样的圆,左边一个右边一个下面一个,三个圆两两相切,然后在这三个圆中间的地方找一个小圆,要求这个小圆与这三个大圆相切,这被称为一次操作,然后第二次操作的时候,就把下面的大圆替换成那个小圆,然后从这两个大圆和一个小圆中间再找一个更小的圆也与它们相切,最后问你经过k次操作后那个小圆的半径是多少。
思路:我们可以把三个圆的圆心两两相连,连成一个三角形,再把三个圆心与中间的小圆心相连,假设左右两个大圆的半径为R,下面的圆半径为r,中间的小圆半径为s,那么就可以转化成下图。
ACM-ICPC 2017南宁赛区网络赛题目题解收集_第1张图片
红色的边都是已知长度的,并且长度都在图中标记了出来,因为相切,所以两圆心的距离等于两圆半径之和。
那么我们可以对图中的蓝色边做文章,蓝色边的长度可以等于sqrt((R+s)^2-R^2),也可以等于sqrt((R+r)^2-R^2)-r-s。这样我们让这两个等式相等,就可以通过R和r求出s了,不过这个方程组里有根号,不是很好解,所以我们可以用二分的方法去寻找方程的解。s求出来后我们就完成了一次操作,然后把s的值赋给r,我们就可以进行第二次操作了。
PS:这道题本来我想先A的,结果看看有点复杂就没上手,赛后一看,炸了,几何题,瞬间爆炸,想打死自己了
下面是AC代码:(瞬间感觉自己数学要重学了)


#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 1e9 + 5;
const int MAXN = 100000007;
const int MOD = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1.0);
LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a%b); }

struct POINT
{
    double x;
    double y;
    POINT(double a = 0, double b = 0) { x = a; y = b; } //constructor 
};

struct LINESEG
{
    POINT s;
    POINT e;
    LINESEG(POINT a, POINT b) { s = a; e = b; }
    LINESEG() { }
};

double multiply(POINT sp, POINT ep, POINT op)
{
    return((sp.x - op.x)*(ep.y - op.y) - (ep.x - op.x)*(sp.y - op.y));
}

bool InsideConvexPolygon(int vcount, POINT polygon[], POINT q) // 可用于三角形! 
{
    POINT p;
    LINESEG l;
    int i;
    p.x = 0; p.y = 0;
    for (i = 0; i// 寻找一个肯定在多边形polygon内的点p:多边形顶点平均值 
    {
        p.x += polygon[i].x;
        p.y += polygon[i].y;
    }
    p.x /= vcount;
    p.y /= vcount;

    for (i = 0; i1) % vcount];
        if (multiply(p, l.e, l.s)*multiply(q, l.e, l.s)<0) /* 点p和点q在边l的两侧,说明点q肯定在多边形外 */
            break;
    }
    return (i == vcount);
}

void find(int x, int y, int &X, int &Y)
{
    double ox, oy;
    POINT pp[6];
    for (int i = -9; i <= 10; i++)
        for (int j = -9; j <= 10; j++)
        {
            oy = 7.5*j;
            ox = 5 * sqrt(3)*i + j*2.5*sqrt(3);
            pp[0].x = ox;
            pp[0].y = oy + 5;
            pp[1].x = ox + 2.5*sqrt(3);
            pp[1].y = oy + 2.5;
            pp[2].x = ox + 2.5*sqrt(3);
            pp[2].y = oy - 2.5;
            pp[3].x = ox;
            pp[3].y = oy - 5;
            pp[4].x = ox - 2.5*sqrt(3);
            pp[4].y = oy + 2.5;
            pp[5].x = ox - 2.5*sqrt(3);
            pp[5].y = oy - 2.5;
            if (InsideConvexPolygon(6, pp, POINT(x, y)))
            {
                X = i;
                Y = j;
                return;
            }
        }
}

int main()
{
    int x, y;
    int X, Y;
    scanf("%d%d", &x, &y);
    find(x, y, X, Y);
    printf("[%d,%d]", X, Y);
    for (int i = 2; i <= 10; i++)
    {
        scanf("%d%d", &x, &y);
        find(x, y, X, Y);
        printf(", [%d,%d]", X, Y);
    }
    printf("\n");
}

本题感谢Effiel大佬的AC代码,附上原作者题解地址:
http://blog.csdn.net/kele52he/article/details/78079145
H.A Cache Simulator(组成原理水题)
题目链接:https://nanti.jisuanke.com/t/17315
这道题如果会组成原理的话,基本上能直接1A,组成原理的链接:
https://wenku.baidu.com/view/6ad6e27f76c66137ee061990.html
下面给出代码:

#include
using namespace std;
typedef long long ll;

ll a[(1<<6)+1];

char s[100];

int main()
{

//    freopen("data.txt","r",stdin);
//    ios_base::sync_with_stdio(false);
    ll ct=0,ct1=0;
    for(int i=0;i<(1<<6);i++)
    {
        a[i]=-1;
    }
    while(~scanf("%s",s))
    {
        if(strcmp(s,"END")==0)
            break;
        ct++;
        int len=strlen(s);
        ll sum=0;
        ll t = 1;
        for(int i=len-1;i>=0;i--)
        {
            if(s[i]>='0'&&s[i]<='9')
            {
                sum += (s[i]-48)*t;
            }
            else if(s[i]>='A'&&s[i]<='F')
            {
                sum += (s[i]-55)*t;
            }
            t*=16;
        }
//        cout << sum<
        ll t1 = sum/(1<<10);
        sum%=(1<<10);

        ll tt = sum>>4;
        sum%=64;

        if(a[tt]==-1)
        {
            printf("Miss\n");
            a[tt] =t1;
        }
        else
        {
            if(a[tt]==t1)
            {
                ct1++;
                printf("Hit\n");

            }
            else
            {
                printf("Miss\n");
                a[tt] =t1;
            }
        }
    }
    printf("Hit ratio = %.2lf%%\n",(double)ct1/(double)ct*100);
    return 0;
}

感谢chudongfang2015大佬的AC代码,给出原链接地址:
http://blog.csdn.net/chudongfang2015/article/details/78080214
I.GSM Base Station Identification(暴力+计算几何)
题目链接:https://nanti.jisuanke.com/t/17316
PS:这道题是我第一个想写的题目,一看就感觉像三角定位法,结果一看不对,是六边形,瞬间脑子一片空白,然后读到最后感觉有点晕,就没写下去,赛后问了大佬之后,我还是感觉自己太naive了,居然可以暴力,瞬间炸了。
题意:有一个坐标系,每格的长度是5,然后在这个坐标系中有许多边长为5的正六边形,这些正六边形互相连接,没有空隙,每个正六边形都有一个自己的坐标,原点处的正六边形坐标是[0,0],然后每往右一个横坐标+1,每往左一个横坐标-1。然后右上方的纵坐标+1,左下方的纵坐标-1。这样每个正六边形都有属于自己的不同的坐标。现在给你一个坐标系中的坐标,问这个坐标所在的正六边形的坐标是多少。
思路:因为正六边形的横坐标和纵坐标的范围都是[-9,10],一共只有400个正六边形,所以我们可以把每一个正六边形都循环一遍,判断当前的这个点是否在该六边形中。现在的问题就是我们怎么判断这个点是否在坐标为[i,j]的正六边形中,我们首先可以根据i和j求出该正六边形的中心坐标,仔细观察后可以发现,oy=7.5*j,ox=5*sqrt(3)*i+2.5*sqrt(3)*j。然后我们可以根据中心的坐标求出周围六个顶点的坐标,之后我们就可以套用判断一个点是否在一个凸多边形内的模板,如果它在里面,那么当前的i和j就是答案坐标。
代码如下:

#include
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int INF = 1e9 + 5;
const int MAXN = 100000007;
const int MOD = 1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1.0);
LL gcd(LL a, LL b) { return b == 0 ? a : gcd(b, a%b); }

struct POINT
{
    double x;
    double y;
    POINT(double a = 0, double b = 0) { x = a; y = b; } //constructor 
};

struct LINESEG
{
    POINT s;
    POINT e;
    LINESEG(POINT a, POINT b) { s = a; e = b; }
    LINESEG() { }
};

double multiply(POINT sp, POINT ep, POINT op)
{
    return((sp.x - op.x)*(ep.y - op.y) - (ep.x - op.x)*(sp.y - op.y));
}

bool InsideConvexPolygon(int vcount, POINT polygon[], POINT q) // 可用于三角形! 
{
    POINT p;
    LINESEG l;
    int i;
    p.x = 0; p.y = 0;
    for (i = 0; i// 寻找一个肯定在多边形polygon内的点p:多边形顶点平均值 
    {
        p.x += polygon[i].x;
        p.y += polygon[i].y;
    }
    p.x /= vcount;
    p.y /= vcount;

    for (i = 0; i1) % vcount];
        if (multiply(p, l.e, l.s)*multiply(q, l.e, l.s)<0) /* 点p和点q在边l的两侧,说明点q肯定在多边形外 */
            break;
    }
    return (i == vcount);
}

void find(int x, int y, int &X, int &Y)
{
    double ox, oy;
    POINT pp[6];
    for (int i = -9; i <= 10; i++)
        for (int j = -9; j <= 10; j++)
        {
            oy = 7.5*j;
            ox = 5 * sqrt(3)*i + j*2.5*sqrt(3);
            pp[0].x = ox;
            pp[0].y = oy + 5;
            pp[1].x = ox + 2.5*sqrt(3);
            pp[1].y = oy + 2.5;
            pp[2].x = ox + 2.5*sqrt(3);
            pp[2].y = oy - 2.5;
            pp[3].x = ox;
            pp[3].y = oy - 5;
            pp[4].x = ox - 2.5*sqrt(3);
            pp[4].y = oy + 2.5;
            pp[5].x = ox - 2.5*sqrt(3);
            pp[5].y = oy - 2.5;
            if (InsideConvexPolygon(6, pp, POINT(x, y)))
            {
                X = i;
                Y = j;
                return;
            }
        }
}

int main()
{
    int x, y;
    int X, Y;
    scanf("%d%d", &x, &y);
    find(x, y, X, Y);
    printf("[%d,%d]", X, Y);
    for (int i = 2; i <= 10; i++)
    {
        scanf("%d%d", &x, &y);
        find(x, y, X, Y);
        printf(", [%d,%d]", X, Y);
    }
    printf("\n");
}

本题感谢Effiel大佬的AC代码,附上原作者题解地址:
http://blog.csdn.net/kele52he/article/details/78079145

J.Minimun Distance in a Star Graph(贪心思想)
题目链接:https://nanti.jisuanke.com/t/17317
题意:他啰里八嗦讲了一大堆。其实就是从a状态变成b状态。
每次只能把第一个数和另一个数交换。求最小的交换次数,从a变到b。
POINT:贪心思想。把b序列变成12345n,把a序列对印b起来。
若第一个数不是1,则把它归位,交换他们 ans++。
若是1,则把1和后面错误的数交换,ans++。
这样交换下去便是最小的交换次数,输出ans。
主要还是读懂题目。
下面给出代码:

#include  
using namespace std;  
int n,ans[10],mean[15];  
int num[15];  
int work()  
{  
    int ans=0;  
    while(1)  
    {  
        if(num[1]!=1)  
        {  
            ans++;  
            swap(num[1],num[num[1]]);  
        }  
        else  
        {  
            int flag=0;  
            for(int i=1;i<=n;i++)  
            {  
                if(num[i]!=i)  
                {  
                    flag=1;  
                    ans++;  
                    swap(num[1],num[i]);  
                    break;  
                }  
            }  
            if(!flag) break;  

        }  
    }  
    return ans;  
}  
int main()  
{  
    while(scanf("%d",&n)!=EOF)  
    {  
        memset(mean,0,sizeof mean);  
        for(int i=1;i<=5;i++)  
        {  
            char s1[15],s2[15];  

            scanf("%s%s",s1,s2);  
            for(int i=0;i'0'] = i+1;  
            }  
            for(int i=0;i1] = mean[s1[i]-'0'];  
            printf("%d\n",work());  
        }  
    }  
    return 0;  
}  

本题AC代码提供者还是Mr_Treeeee,感谢Mr_Treeeee的AC代码,下面给出原代码链接:
http://blog.csdn.net/mr_treeeee/article/details/78078435

K.Line Segments Clipped by Windows
题目链接:https://nanti.jisuanke.com/t/17318
本题暂时还没有找到AC代码,待补
L.The Heaviest Non-decreasing Subsequence Problem(LIS,最长不递减子序列)
题目链接:https://nanti.jisuanke.com/t/17319
PS:这是我们最后死磕的一道题,2小时还没A,感觉心态真的爆炸,一上手看到这题就说是LIS,然后套模板,感觉不对又重新看了一边题目,发现还是LIS没变,模板改了20+,交上去不是WA就是CE,赛后问了才知道是要拆开解决,晕了。
题意:最长上升子序列。但是每个数的权值不一样,有的是1,有的是0,有的是5。求最大权值的最长上升子序列。
POINT:把权值为0的去掉,把权值为5的复制5个放进数组。
再跑一遍nlogn的最长上升子序列,就是答案。
下面给出代码:

#include 
using namespace std;  
#define LL long long  
const LL maxn = 1e6+888;  
int n=0;  
int num[maxn];  
int a[maxn];  
int main()  
{  
    int q;  
    while(~scanf("%d",&q))  
    {  
        int flag=0;  
        if(q>=10000)  
        {  
            q=q-10000;  
            flag=1;  
        }  
        if(q<0) continue;  
        if(flag)  
        {  
            for(int i=1;i<=5;i++)  
            {  
                num[++n]=q;  
            }  
        }  
        else  
            num[++n]=q;  
    }  
    if(n==0)  
    {  
        printf("0\n");  
        return 0;  
    }  
    int now=1;  
    a[1]=num[1];  
    for(int i=2;i<=n;i++)  
    {  
        if(num[i]>=a[now]) a[++now]=num[i];  
        else  
        {  
            int l=upper_bound(a+1,a+1+now,num[i])-a;  
            a[l]=num[i];  
        }  
    }  
    printf("%d\n",now);  
}  

本题AC代码提供者还是Mr_Treeeee,感谢Mr_Treeeee的AC代码,下面给出原代码链接:
http://blog.csdn.net/mr_treeeee/article/details/78078435

M.Frequent Subsets Problem(进制运算或者集合)
题目链接:https://nanti.jisuanke.com/t/17320
PS:看到这题,本来想说怎么感觉有点复杂,看来还是英文没读懂,晕,赛后一问,进制运算/集合,整个人都不好了,突然想打人。MMP
题意:
给你一个n和a。n代表数据里只有1-n值的数字。
给你m个集合,让你求出存在几个子集,他在这m个集合里出现的次数>=a*m。
我们先来看一下进制运算的想法。
因为n只到20,直接利用二进制的位运算暴力解决,1代表有该元素,0代表没有该元素
给出代码:

#include
using namespace std;
typedef long long ll;


int n;
double m;
stringstream ss;
string s;
int t;
int c[55];
int sum=0;
int main()
{
//    freopen("data.txt","r",stdin);
    ios_base::sync_with_stdio(false);
    memset(c,0,sizeof(c));
    getline(cin,s);
    ss<>n>>m;
    while(getline(cin,s))
    {
        ss.clear();
        ss<while(ss >> t)
        {
            c[sum] |= ( 1<<(t-1) );
        }
//        cout <
        sum++;
    }
    int xx = ceil(m*sum);
    int ans = 0;
    int maxx = (1<for(int i=1;i<=maxx;i++)
    {
        int ct = 0;
        for(int j=0;jif((i&c[j])==i)
            {
//                cout <<(i&c[j])<<" "<
                ct++;
            }
        }
//        if(ct)cout<
        if(ct>=xx) ans++;
    }
    cout<return 0;
}

该想法由chudongfang2015给出,感谢这位大佬的AC代码,下面给出链接:
http://blog.csdn.net/chudongfang2015/article/details/78082970

下面我们来看一下集合思想的做法:
一个一个找,找1,1如果可以作为答案,找1 2。1 2不行找1 3。1 2行就找1 2 3。
接着找2,2可以找2 3,2 3不行找2 4。
这样需要剪枝,找1的时候肯定只存在在某几个集合中,做一个标记,继续找下去只要在这几个集合中找就行了。再继续更新标记,范围会越来越少
给出代码:

#include
using namespace std;
int flag[55][22];
int ans;
int num,temp,n;
void work(int x,int aim[])
{
    int nn=0;
    int faim[55];
    for(int i=1;i<=num;i++)
        faim[i]=aim[i];
    for(int i=1;i<=num;i++)
    {

        if(flag[i][x]&&faim[i])
        {
            faim[i]=1;
            nn++;
        }
        else
            faim[i]=0;
    }
    if(nn>=temp)
    {
        ans++;
        for(int i=x+1;i<=n;i++)
        {
            work(i,faim);
        }
    }
}
int main()
{
    double a;
    scanf("%d %lf",&n,&a);
    int d;
    num=1;
    while(~scanf("%d",&d))
    {
        char c;
        scanf("%c",&c);
        flag[num][d]=1;
        if(c=='\n')
            num++;
    }
    num--;
    temp=ceil((double)num*a);
    ans=0;
    for(int i=1;i<=n;i++)
    {
        int aim[55];
        for(int i=1;i<=num;i++) aim[i]=1;
        work(i,aim);
    }
    printf("%d\n",ans);
    return 0;
}

本题AC代码提供者还是Mr_Treeeee,感谢Mr_Treeeee的AC代码,下面给出原代码链接:
http://blog.csdn.net/mr_treeeee/article/details/78078435

以上就是ACM-ICPC2017年南宁网络赛的题目及其题解收集了,有些题目由于还没有找到题解,所以还是待补状态,等有大佬放在网上之后再进行编辑。感谢网上给出AC代码的大佬们。
今年的ACM网络赛估计没了,上海的EC不知道是不是没有网络赛,如果没有,那南宁是今年ACM网络赛的最后一场了。我们2018ACM见

你可能感兴趣的:(DFS,BFS,水题,贪心,ACM-ICPC)