洛谷 P3154 [CQOI2009]循环赛 题解

u1s1,我的代码在洛谷上只能拿88pts,但在其他OJ上能过,所以如果你想在洛谷上通过这道题那请出门左转。


这道题看题面和数据范围就知道是搜索加一些神奇的剪枝。

我一开始的想法是按人枚举,但发现这样连裸的搜索都很难写,所以我转化了一下思路,枚举比赛(即用 ( x , y ) 来表示比赛双方)。

这样的话裸的搜索就很好写了,分 y = n 和  y != n 两种情况讨论就好了。

接下来我们来思考几个剪枝:

1. 如果当前某一方加分后超过最终分数,可以剪枝。

2. 如果某一方即使后面的比赛全赢也无法到达最终分数,可以剪枝。

3. 如果当前是某个人的最后一场比赛,可以直接推出结果。

然后一一实现就好啦~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define maxn 10 
#define rep(i,s,e) for(register int i=s;i<=e;++i)
#define dwn(i,s,e) for(register int i=s;i>=e;--i)
using namespace std;
inline int read()
{
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9') {x=x*10+c-'0'; c=getchar();}
    return f*x;
}
inline void write(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
int n,ans;
int s[maxn],f[maxn];
inline bool check()
{
    rep(i,1,n)
        if(s[i]!=f[i]) return 0;
    return 1;
}
void dfs(int x,int y)
{
    if(x==n)
        if(check()==1) {++ans; return;}
    if(s[x]+3*(n-y+1)<f[x]) return;//剪枝一
    if(y==n)//剪枝二
    {
        if(f[x]-s[x]==1)
        {
            s[x]++;s[y]++;
            dfs(x+1,x+2);
            s[x]--;s[y]--;
        }
        else if(f[x]-s[x]==0)
        {
            s[y]+=3;
            dfs(x+1,x+2);
            s[y]-=3;
        }
        else if(f[x]-s[x]==3)
        {
            s[x]+=3;
            dfs(x+1,x+2);
            s[x]-=3;
        }
    }
    if(s[x]+3<=f[x])//剪枝三
    {
        s[x]+=3;
        dfs(x,y+1);
        s[x]-=3;
    }
    if(s[y]+3<=f[y])//剪枝三
    {
        s[y]+=3;
        dfs(x,y+1);
        s[y]-=3;
    }
    if(s[x]+1<=f[x]&&s[y]+1<=f[y])//剪枝三
    {
        s[x]++;s[y]++;
        dfs(x,y+1);
        s[x]--;s[y]--;
    }
}
signed main()
{
    n=read();
    rep(i,1,n) f[i]=read();
    dfs(1,2);
    write(ans);
    return 0;
}
原文地址:https://www.cnblogs.com/handsome-zyc/p/13623886.html