1. 程式人生 > >luogu 狗哥玩木棍

luogu 狗哥玩木棍

dfs作為暴力演算法,有時候剪枝好了可以騙很多分,因此一定要多思考剪枝,註釋夠詳細了吧QAQ~

#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,a[21],nxt[21],lenth,sum;
bool vis[21];
int flag;
bool cmp(int x,int y) {
    return x>y;
}
void dfs(int k,int last,int rest) {//k為拼到第幾根,last為上次用的是第幾根木棍,rest表示該木料剩餘多少 
    int i;
    if(!rest) {
        if(k==4) {
            flag=1;
            return ;
        }
        for( i=1; i<=m; i++)
            if(!vis[i])
                break;
        vis[i]=1;
        dfs(k+1,i,lenth-a[i]);
        vis[i]=0;
        if(flag) return ;
    }
    int l=last+1,r=m,mid;//二分查詢第一根比剩餘短的木棍 
    while(l<r) {
        mid=(l+r)/2;
        if(a[mid]<=rest) r=mid;
        else l=mid+1;
    }
    for( i=l; i<=m; i++) {
        if(!vis[i]) {
            vis[i]=1;
            dfs(k,i,rest-a[i]);
            vis[i]=0;
            if(flag) return ;
        }
    }

}
int main() {
    cin>>n;
    for(int i=1; i<=n; i++) {
        cin>>m;
        sum=0;
        for(int j=1; j<=m; j++) {
            cin>>a[j];
            sum+=a[j];
        }
        if(sum%4!=0) {//如果不能被4整除,邊長一定不是整數,肯定拼不出
            printf("no\n");
            continue;
        }
        fill(vis+1,vis+21,0);//多組資料,要初始話 
        sort(a+1,a+m+1,cmp);//按從大到小排列,先用大的,小的肯定比大的靈活,能不用就不用,貪心思想
        for(int j=1; j<=m; j++)
            nxt[j]=j;
        for(int j=m-1; j; j--)
            if(a[j]==a[j+1]) nxt[j]=nxt[j+1];//因為木棍可能有相同的,所以開個nxt陣列,一根不行的話可以直接去找下一根不一樣長度的
        lenth=sum/4;//正方形邊長
        if(a[1]>lenth) {//如果第一根木棍就比邊長大,也拼不出
            printf("no\n");
            continue;
        }
        flag=0;
        vis[1]=1;
        dfs(1,1,lenth-a[1]);
        vis[1]=0;
        if(flag==1) printf("yes\n");
        else printf("no\n");
    }
}