HDU 6223 Infinite Fraction Path(level 3)(bfs+剪枝/倍增)

題目鏈接

題意:

給你一串長度爲n的數字,對於裏面的每一個位置,第i個位置與第(i*i+1)%n個位置有一條單向的路徑

你可以從任意位置出發,走n次,得到n個字符組成的數字

讓你輸出你能得到的最大的數字

解析:

這道題做的時候真的想爆了..什麼反向建邊變成一棵樹,再用樹上倍增..還有tarjan縮點...什麼想法都想了..

但是後來看題解發現這是暴力直接做......要說線索可以打一下表,對於n<=150000,好像環的數量應該小於兩位數,

大部分是一位數。很多都是沒有入度的點.....

所以這裏暴力加剪枝就可以過。

其實觀察那種條件很苛刻的,你自己都舉不出很難的樣例來使複雜度增大的情況下,可以嘗試暴力+剪枝。

譬如說,這裏找最大的字符串,你很難舉出那種一直比到第n個位置才分出大小的字符串,就比兩個還比較好舉,但是當

需要比較的字符串一多,很多串大多都在中間或前面的位置被確定大小了。所以這種題目都可以嘗試暴力,只是需要合理剪枝。

先說題解的做法吧,bfs,對於同一層有兩個剪枝1.對於同一層,除了值最大的點可以向下找,其他都剪掉

2.對於同一層,如果在值最大的點中,存在相同的點,那麼把這些相同的點都刪掉只剩一個。即,上一層中

有很多最大值點,都指向同一個點。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <bitset>
using namespace std;

typedef long long ll;

const int MAX = 150000+100;

int n;
char s[MAX];
int mp[MAX];
int val[MAX];

char ans[MAX];
int Max;
int vis[MAX];


typedef struct node
{
	int id;
	int val;
	int step;
	int ord;
	int nxtval;
	node(int ii=-1,int vv=-1 ,int ss=-1,int oo=0,int nxt=0):id(ii),val(vv),step(ss),ord(oo),nxtval(nxt){}
	friend bool operator <(node a,node b)
	{
		if(a.step==b.step) return a.nxtval<b.nxtval;
		return a.step>b.step;
	}
}node;

priority_queue<node> mq;
int vit[MAX];


void bfs()
{
	ans[0]=Max+'0';
	for(int i=1;i<n;i++)
		ans[i]='0';
	while(!mq.empty()){
	
		node u=mq.top();
		mq.pop();
		if(!vis[u.ord]) continue;

		if(u.step==n)
		{
			return;
		}
		node now;
		now.id=mp[u.id];
		
		now.step=u.step+1;
		now.val=val[now.id];
		now.ord=u.ord;
		now.nxtval=val[mp[now.id]];

		if(vit[now.id]==now.step) continue;

		if(now.val+'0'>=ans[now.step])
		{
			mq.push(now);
			ans[now.step]=now.val+'0';
			vit[now.id]=now.step;
		}
		else
		{
			vis[u.ord]=0;
		}
	}
}



int main()
{
    int T;
    int cnt=0;
    scanf("%d",&T);
    int cas=0;
    while(T--)
    {
		memset(vis,0,sizeof(vis));
		memset(vit,0,sizeof(vit));
        cas++;
        cnt=0;
        scanf("%d",&n);
        //getchar();
        scanf("%s",s);
		while (!mq.empty())
		{
			mq.pop();
		}
        Max=0;
        
        for(int i=0;i<n;i++){
            mp[i]=(1ll*i*i+1)%n;
            val[i]=s[i]-'0';
            Max=max(Max,val[i]);
        }
		
        for(int i=0;i<n;i++){
            if(val[i]==Max){
				
				mq.push(node(i,val[i],0,cnt,val[mp[i]]));
				vis[cnt++]=1;
            }
        }
        
		//if(cnt<n){
			
			
		//}
        printf("Case #%d: ",cas);
        
		bfs();
		for(int i=0;i<n;i++)
			printf("%c",ans[i]);
        printf("\n");
    }
}

其實關鍵就是第二個剪枝,當深度爲x時,走到了w點,但是前面已經存在當深度爲x,走到w點的狀態,那麼接下來這兩種狀態

都是一樣的,所以可以剪掉。所以我把之前寫的迭代版,加了has,來存這個狀態。也過了,雖然用2.5s,因爲map二分有常數

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <string>
#include <bitset>
using namespace std;

typedef long long ll;

const int MAX = 150000+100;

int n;
char s[MAX];
int mp[MAX];
int val[MAX];
int hd[MAX];
int cas;

map<int,int> has[MAX];


bool judge(int now,int ind)
{
    int flag=0;
    int cnt=0;
    int h1=0,h2=0;
	int tim=0;
    while (tim<n)
    {
		tim++;
		if(has[ind].count(tim)) 
		{
			flag=0;
			break;
		}
		has[ind][tim]=1;
        if(now==ind)
        {
            flag=0;
            break;
        }
        if(s[now]<s[ind])
        {
            flag=1;
            break;
        }
        else if(s[now]>s[ind])
        {
            flag=0;
            break;
        }

        now=mp[now];
        ind=mp[ind];
		
        
    }
    if(!flag) return 0;
    else return 1;
}

int main()
{
    int T;
    int cnt=0;
    scanf("%d",&T);
    cas=0;
    while(T--)
    {
        cas++;
        cnt=0;
        scanf("%d",&n);
        //getchar();
        scanf("%s",s);
        int Max=0;
        
        for(int i=0;i<n;i++){
            mp[i]=(1ll*i*i+1)%n;
            val[i]=s[i]-'0';
            Max=max(Max,val[i]);
			has[i].clear();
        }
        for(int i=0;i<n;i++){
            if(val[i]==Max){
                hd[cnt++]=i;
            }
        }
        int now=hd[0];
        
        if(cnt<n){
            
            for(int i=1;i<cnt;i++){
                if(judge(now,hd[i]))
                    now=hd[i];
            }
        }
        printf("Case #%d: ",cas);
        for(int i=0;i<n;i++)
        {
            printf("%c",s[now]);
            now=mp[now];
        }
        printf("\n");
    }
}

做法二:就是用倍增的思想,nxt[i][j]表示i走2^j步到達的點

st[i][j]表示[i,i走2^j到達的點)中間的路徑的字符串的hash值

然後因爲字符串hash只能判斷兩個字符串是否相等,不能判斷大小

所以我們通過每一層比較st[x][len]與st[x][len]是否相等,

不相等的話,如果前一部分有不相等,則遞歸到前一部分

否則到後一部分。

一直到len=0時,直接比較兩個字符s[x]<s[y]

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int N = 150000+100;
const int MOD = 1e9+7;
char s[N];
ll base=10;
ll p[N*2];
int nxt[N][20];
ll st[N][20];
int max0=18;
int bit[31];
int mp[N];

void init_hash()
{
    p[0] = 1;
    for(int i = 1; i <N+N; i ++)p[i] =p[i-1] * base%MOD;
    bit[0]=1;
    for(int i=1;i<=max0;i++) bit[i]=bit[i-1] * 2;
    //1235->1,12,123,1235
}

void init_st(int n)
{
    memset(nxt,0,sizeof(nxt));
    memset(st,0,sizeof(st));
    for(int i=1;i<=n;i++)
        nxt[i][0]=mp[i];
    for(int j=1;j<=max0;j++)  //[i,i的第2^j次方個父親)
        for(int i=1;i<=n;i++)
            nxt[i][j]=nxt[nxt[i][j-1]][j-1];
    for(int i=1;i<=n;i++)
        st[i][0]=s[i]-'0';
    for(int j=1;j<=max0;j++)
        for(int i=1;i<=n;i++)
            st[i][j]=(st[i][j-1]*p[bit[j-1]]%MOD+st[nxt[i][j-1]][j-1])%MOD;


}

/*ull get(int l, int r, ull g[]){//取出g裏l - r裏面的字符串的hash值
    return g[r] - g[l - 1] * p[r - l + 1];
}*/

int judge(int x,int y,int len)
{
    if(len==0)
    {
        return s[x]<s[y];
    }

    if(st[x][len]==st[y][len])
        return 0;
    else if(st[x][len-1]!=st[y][len-1])
    {
        return judge(x,y,len-1);
    }
    else
    {
        return judge(nxt[x][len-1],nxt[y][len-1],len-1);
    }

}


int main()
{
    int t;
    scanf("%d",&t);
    init_hash();
    int cas=0;
    while(t--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s",s+1);
        for(ll i=0;i<n;i++)
        {
            mp[i+1]=(i*i+1)%n+1;
        }
        init_st(n);
        int now=1;
        for(int i=2;i<=n;i++)
        {
            if(judge(now,i,max0))
                now=i;
        }
        cas++;
        printf("Case #%d: ",cas);
        for(int i=1;i<=n;i++)
        {
            printf("%c",s[now]);
            now=mp[now];
        }
        printf("\n");
        //for(int i = 0; i < n; i ++)
        //    has[i] = has[i - 1] * base + (s[i] - '0');


    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章