1. 程式人生 > >nyoj 1078 漢諾塔(四)[二分圖 || 規律 || 暴力 || 貪心]

nyoj 1078 漢諾塔(四)[二分圖 || 規律 || 暴力 || 貪心]

二分圖 二分圖匹配 int 處理 names 特殊 mes while 最小路徑覆蓋

題目:nyoj 1078 漢諾塔(四)


分析:做這個題目的時候是在圖論的題目裏面看到的。到時讀了題目推了一下,發現好像有點規律。試了一下果然過了。


後來看了一下數據,才50。那麽試了一下模擬,也過了。

好像zoj有一道題目卡模擬,模擬的時候必須貪心一下才幹過


這道題出題人的意圖在於考大家的:二分圖最小路徑覆蓋。

把每個球看做一個點,然後假設兩個和為平方數的話就給這兩個點之間連接一條邊,然後用一個特殊的匹配算法。類似於匈牙利算法。可是每次找匹配的時候增加一條邊上連接的有匹配的話就不能匹配,最後求一個最大匹配就好了。這個題目50個點,當然要預處理成離線的。

假設每次都建圖跑的話時間復雜度是非常高的,所以我們能夠用標記的方法。假設匹配過的就不用再匹配了。

這樣能減少非常多時間,可是還是非常長。


找規律代碼:

 
#include <cstdio>
int ans[55];
void isit()
{
    ans[1]=1,ans[2]=3;
    for(int i=3;i<51;i++)
        ans[i]=ans[i-1]+(i+1)/2 * 2;
}
int main()
{
    int T,n;isit();
    scanf("%d",&T);
    for(;T--&&scanf("%d",&n);printf("%d\n",ans[n]));
}
        


暴力代碼:

 
#include <cstdio>
#include <stack>
#include <cmath>
using namespace std;
stack<int> st[60];
int ans[60];
void isit()
{
    int tmp=1,i=1;
    while(i<55)
    {
        int ok=0;
        for(i=1;!st[i].empty();i++)
        {
            int zhi = st[i].top()+tmp;
            int sq=sqrt(zhi);
            if(sq * sq == zhi)
            {
                st[i].push(tmp);
                ok=1;
                break;
            }
        }
        if(!ok){
            st[i].push(tmp);
            ans[i]=tmp-1;
        }
        tmp++;
    }
}
int main()
{
    int T,n;isit();
    scanf("%d",&T);
    for(;T--&&scanf("%d",&n);printf("%d\n",ans[n+1])){}
}
        


二分圖匹配代碼:

 
#include <cstdio>
#include <stack>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 60;
const int M = 1500;
int ans[N];
bool  ok[M*2+10];
bool mp[M][M];
int mx[M],my[M]; //mx保存正向邊 my保存反向邊
bool used[M]; //
bool dfs(int v) {
     for (int u = 1; u < v; ++u) //優化
         if (mp[v][u] && !used[u]) {
            used[u] = true;
             if (my[u] == -1 || dfs(my[u])) {  //一直向下找
                 my[u] = v;
                 mx[v] = u;
                 return true;
             }
         }
     return false;
}
int Max_Pi(int ans,int n)  //最大匹配
{
    for(int i=1;i<=n;i++)
    {
        if(mx[i]<0){
            memset(used,false,sizeof(used));
            if(dfs(i))
                ans++;
        }
    }
    return ans;
}
void Min_Road()  //最小路徑覆蓋
{
    memset(mx,-1,sizeof(mx)); //優化
    memset(my,-1,sizeof(my));
    for(int i=1;i*i<=2*M;i++)
        ok[i*i]=true;
    ans[1]=1;
    int tmp=0;
    for(int i=2;i<=M;i++)
    {
        for(int j=1;j<i;j++)
            if(ok[i+j])
                mp[i][j]=true;
        tmp=Max_Pi(tmp,i);
        //printf("xx%d %d",i,tmp);
        ans[i-tmp]=i;
        if(i-tmp>55)
            break;
    }
//    for(int i=1;i<=50;i++)
//        printf("%d ",ans[i]);
}
int main()
{
    int T,n;Min_Road();
    scanf("%d",&T);
    for(;T--&&scanf("%d",&n);printf("%d\n",ans[n])){}
}
        


nyoj 1078 漢諾塔(四)[二分圖 || 規律 || 暴力 || 貪心]