HDU5937 Equation 【DFS+剪枝】

5 篇文章 0 订阅
4 篇文章 0 订阅
Equation

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 472    Accepted Submission(s): 137


Problem Description
Little Ruins is a studious boy, recently he learned addition operation! He was rewarded some number bricks of 1 to 9 and infinity bricks of addition mark '+' and equal mark '='.

Now little Ruins is puzzled by those bricks because he wants to put those bricks into as many different addition equations form x+y=z as possible. Each brick can be used at most once and x, y, z are one digit integer.

As Ruins is a beginer of addition operation, x, y and z will be single digit number.

Two addition equations are different if any number of x, y and z is different.

Please help little Ruins to calculate the maximum number of different addition equations.


Input
First line contains an integer T, which indicates the number of test cases.

Every test case contains one line with nine integers, the ith integer indicates the number of bricks of i.

Limits
1≤T≤30
0≤bricks number of each type≤100


Output
For every test case, you should output 'Case #x: y', where x indicates the case number and counts from 1 and y is the result.


Sample Input
3
1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2
0 3 3 0 3 0 0 0 0


Sample Output
Case #1: 2
Case #2: 6
Case #3: 2


Source
2016年中国大学生程序设计竞赛(杭州)

一共有36种情况
每个等式选/不选 O(236) 肯定T到爆

1+5=6 && 5+1=6 可以看做同一个等式选择了2次
暴力掉1+1,2+2,3+3,4+4
一共是 O(31624) ,复杂度有所改观,但是还是太高

然后考虑剪枝:
①:maxAns=所有数字个数/3,选出的最大等式数ans==maxAns,不可能有更大了,直接返回
②:如果当前已经选择了的等式数+剩下可以选择的等式数<=ans,不可能比ans大了,直接退出这次dfs

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<string>
#include<vector>
#include<deque>
#include<queue>
#include<algorithm>
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<string.h>
#include<math.h>
#include<list>

using namespace std;

#define ll long long
#define pii pair<int,int>
const int inf = 1e9 + 7;

const int N = 1e5+5;

inline int read(){
    int x;
    char ch;
    while(!isdigit(ch=getchar()));
    x=ch-'0';
    while(isdigit(ch=getchar())){
        x=x*10+ch-'0';
    }
    return x;
}

int num[10];
int maxAns=0;

int tn[4][3]={{1,1,2},{2,2,4},{3,3,6},{4,4,8}};//1+1=2,2+2=4,......

int ans=0;

void dfs(int i,int j,int k,int canSelect){//当前选到i+j这个等式,(i<j),k=已经选了的等式数,canSelect=剩下的能选择的最大等式数
    if(ans>=maxAns||canSelect+k<=ans){
        return;
    }
    if(i+j>9){
        i+=1;
        j=i+1;
    }
    if(i>=5){
        int n1=num[1],n2=num[2],n3=num[3],n4=num[4],n6=num[6],n8=num[8];
        for(int z=0,end=1<<4;z<end;++z){
            int tk=k;
            num[1]=n1,num[2]=n2,num[3]=n3,num[4]=n4,num[6]=n6,num[8]=n8;
            for(int x=0;x<4;++x){
                if(z&(1<<x)){
                    --num[tn[x][0]];
                    --num[tn[x][1]];
                    --num[tn[x][2]];
                    if(num[tn[x][0]]>=0&&num[tn[x][1]]>=0&&num[tn[x][2]]>=0){
                        ++tk;
                    }
                    else{
                        tk=-1;
                        break;
                    }
                }
            }
            ans=max(tk,ans);
        }
        num[1]=n1,num[2]=n2,num[3]=n3,num[4]=n4,num[6]=n6,num[8]=n8;
        return;
    }
    int&a=num[i],&b=num[j],&c=num[i+j];
    --a,--b,--c;
    if(a>=0&&b>=0&&c>=0){
        dfs(i,j+1,k+1,canSelect-2);
        --a,--b,--c;
        if(a>=0&&b>=0&&c>=0){
            dfs(i,j+1,k+2,canSelect-2);
        }
        ++a,++b,++c;
    }
    ++a,++b,++c;
    dfs(i,j+1,k,canSelect-2);
}

int main()
{
    //freopen("/home/lu/Documents/r.txt","r",stdin);
    //freopen("/home/lu/Documents/w.txt","w",stdout);
    int T;
    scanf("%d",&T);
    for(int t=1;t<=T;++t){
        maxAns=0;
        for(int i=1;i<=9;++i){
            scanf("%d",&num[i]);
            maxAns+=num[i];
        }
        maxAns/=3;
        ans=0;
        dfs(1,2,0,36);
        printf("Case #%d: %d\n",t,ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值