1. 程式人生 > >poj 5943 Kingdom of Obsession (二分圖匹配+素數間隔小知識)

poj 5943 Kingdom of Obsession (二分圖匹配+素數間隔小知識)

題目連結:poj 5943

題意:給定n,s,現在讓1-n和s+1,s+2...s+n這兩堆數匹配,如果(s+i)%j==0那麼s+i就可以和j構成一個匹配,求是否能讓所有的n個數字都構成匹配。

題解:首先我們看到匹配,就應該先想到是不是關於二分圖匹配的問題?顯然這是的,但因為n太大,會T和超記憶體,那麼我們可以想下怎麼去優化,通過打表我們可以知道在1e9範圍,兩素數直接的間隔不超過300,網上有論文說不超過246,隨便啦,不影響我們最後的結果,所以答案就出來了,當n大於300時,直接輸出No,因為此時素數肯定至少有兩個,又因為素數只能被1和本身整除,而此時元素值又大於位置值,故位置1只能被一個素數放,相應的另外一個就放不了了。

此時還有個特殊情況,當n>s時,故有些位置值等於某元素值,那麼我們就可以將這些特殊的就放一起,這樣就會出現兩個素數存在但還會有解的情況,例如 n=5 s=4,->5 6 7 8 9 ,5和7是素數,但5可以直接放在位置5,故也不影響結果。

所以這時我們交換下n和s就行了。

 

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int maxn=500;

vector<int> G[maxn];

int match[maxn];
bool check[maxn];

bool dfs(int u)
{
    int item=G[u].size();
    for(int i=0;i<item;i++)
    {
        int v=G[u][i];

        if(!check[v]){

            check[v]=1;

            if(match[v]==-1||dfs(match[v])){
                match[v]=u;
                return 1;
            }
        }
    }

    return 0;
}

int hungarian(int num_left)
{
    int ans=0;

    memset(match,-1,sizeof(match));

    for(int u=1;u<=num_left;u++)
    {
        memset(check,0,sizeof(check));
        if(dfs(u)) ans++;
    }

    return ans;
}

int main()
{
    int ncase;

    scanf("%d",&ncase);

    for(int T=1;T<=ncase;T++)
    {
        int n,s;

        scanf("%d%d",&n,&s);
        
        if(n>s) swap(n,s); 
        if(n>300){
            printf("Case #%d: No\n",T);continue;
        }

        

        for(int i=1;i<=n;i++)
        G[i].clear();

        for(int i=1;i<=n;i++) 
        {

            int item=s+i;///元素值

            for(int j=1;j<=n;j++){ ///位置
                if(item%j==0) G[j].push_back(i);
            }
        }

        int sum=hungarian(n);
        if(sum==n){
            printf("Case #%d: Yes\n",T);
        }
        else{
            printf("Case #%d: No\n",T);
        }
    }
    return 0;
}