LuoguP1286《两数之和》题解

来源:互联网 发布:c语言开源项目 编辑:程序博客网 时间:2024/05/21 00:19

来先看看题面吧!https://www.luogu.org/problem/show?pid=1286

讲解之前先%%%一下t0vd大佬!详见讨论:这道题有什么思路吗

接下来。。。看懂了我就不讲了吧。。。才怪

虽然大佬很厉害,但我还是再详细地讲一下思路吧,可能语言有点乱,勉强看看吧。我们可以轻松得出,s1=1,s2=2,s3=3和s1=3,s2=2,s3=1是一样的解,输出都是:1 2 3,我们可以利用这个特性进行判重和找答案。

先说找答案。其实这是个枚举。。。我们可以设输入的数组为a数组,假设a[1]一定为s1+s2(s是答案),我们可以枚举表示s1+s3的数,再枚举表示s2+s3的数,那么这样我们就可以得出s1,s2,s3的数值。怎么求看下面,会求的跳过。

我们把(s1+s2)+(s1+s3)+(s2+s3)加起来,那么去括号后为s1+s1+s2+s2+s3+s3,除以2后就等于s1+s2+s3了对吧,我们把它设为s。那么s1=s-(s2+s3),s2=s-(s1+s3),s3=s-(s1+s2),那么三个数就都求出来了。

现在我们已经求出了s1,s2,s3,如果n=3,那么直接输出就行了,记得要退出除了输入之外的所有循环,因为一个输入只输出一个答案。如果n>3,那么我们就枚举表示s1+s4,s5,s6,…sn的数,我用的是dfs,记得枚举过的数要判重,dfs完了判重一定一定要取消!

正如讨论中所说,我们边做边判断当前答案是否合法,不然到答案都出来了再判重就会tle了。引用讨论《这道题有什么思路吗》中大佬的话:“一边搜的过程一边剪枝,比如枚举到x4发现x2+x4并没有在输入的数中出现过,那么就可以剪掉。这样就能很快出解了。”这样我们就可以得出答案并且输出了。

好吧,的确挺乱的,看看代码吧(不过我的代码也挺难看懂的)有可能有些语句是没用的呵呵。。。

#include <iostream>
#include <algorithm>
using namespace std;
int pc[101],a[101],s[101],n,b[100001];
bool pd;
int print()
{
    if(pd==true)return 0;
    pd=true;
    int ss[101];
    for(int i=1;i<=n;i++)
    ss[i]=s[i];
    sort(ss+1,ss+n+1);
    for(int i=1;i<=n;i++)
    cout<<ss[i]<<" ";
    cout<<endl;
}//这是输出函数,先排序后输出应该都懂吧
int dfs(int sy,int w)
{
    if(pd==true)return 0;//如果当前询问已经有答案了,你还找啥答案
    if((a[w]-s[1])<0)return 0;//如果当前数为负数,不合法,退出
    for(int i=1;i<sy;i++)
    if(b[(a[w]-s[1])+s[i]]==0)return 0;//如果不存在当前数+前面任意一个数,那么不合法,退出
    s[sy]=a[w]-s[1];//赋值
    pc[w]=1;//判重标记
    if(sy>=n){print();pc[w]=0;return 0;}//如果找到答案,输出,取消判重,询问结束
    for(int i=1;i<=n*(n-1)/2;i++)
    if(pc[i]==0)dfs(sy+1,i);//枚举能当下一个数的数字
    pc[w]=0;//去判重标记
    return 0;
}
int ans(int n)
{
    pd=false;//判断有没有答案
    for(int i=1;i<=100;i++)
    pc[i]=a[i]=s[i]=0;//初始化
    for(int i=1;i<=100000;i++)b[i]=0;//初始化
    int p[101][101]={0};//必须要={0},如果没有的话无法归0
    for(int i=1;i<=n*(n-1)/2;i++)
    cin>>a[i],b[a[i]]=1;//输入,标记输入的数字
    sort(a+1,a+n*(n-1)/2+1);
    int s12=a[1];//设第一个输入的数字为s1+s2
    for(int i=2;i<=n*(n-1)/2;i++)//枚举s1+s3
    {
        if(pd==true)return 0;
        int s13=a[i];
        for(int j=2;j<=n*(n-1)/2;j++)//枚举s2+s3
        {
            if(pd==true)return 0;
            if(i==j)continue;
            if(p[i][j]==1)continue;
            int s23=a[j];
            if((s12+s13+s23)%2==1)continue; 
            int zs=(s12+s13+s23)/2;
            s[3]=zs-s12,s[2]=zs-s13,s[1]=zs-s23;
            pc[i]=1;pc[j]=1;pc[1]=1;
            if(s[1]<0||s[2]<0||s[3]<0)continue;//以上算s1,s2,s3
            p[i][j]=1;p[j][i]=1;//貌似没啥用

            if(n==3)
            {
                pd=true;
                sort(s+1,s+4);
                cout<<s[1]<<" "<<s[2]<<" "<<s[3]<<endl;
                pc[i]=0;pc[j]=0;pc[1]=0;
                return 0;
            }//如果N=3,直接排序输出,退出询问
            else
            for(int i=1;i<=n*(n-1)/2;i++)
            if(pc[i]==0)dfs(4,i);//否则dfs枚举
            pc[i]=0;pc[j]=0;pc[1]=0;//去判重记号
        } 
    }
    if(pd==false)cout<<"Impossible"<<endl;//如果并没有发现答案,输出Impossible
    return 0;
}
int main()
{
    while(cin>>n)ans(n);//分别处理每个询问
}

原创粉丝点击