• SP2885 WORDRING


    这道题是能够一眼看出思路的(然而我是错的),就是想求出所有的环,然后在所有环中比较出环串的平均长度最长的那一个,然后就完了

    但是很明显这个东西基本无法实现(或者是我单纯太弱),因为无论我们想求平均长度最长,我们肯定希望跑最长路,刚好边权为字符串长度,那么跑进去正环之后就跑不出来了,所以这个想法换掉

    那么继续想,我们无法枚举环来求出答案,我们就试着枚举答案,然后求出是否有满足答案的环就可以了

    若我们此时枚举的答案为(ans),有(k)个字符串,那么就有

    (ans * k = len1 + len2 + len3 + ... + lenk)

    那道这个式子之后,我们对它进行移项

    (0=len1-ans+len2-ans+len3-ans+...+lenk-ans)

    那么对于满足以下式子,也可以判断是正环

    (0 leq len1-ans+len2-ans+len3-ans+...+lenk-ans)

    那么对于这个(ans)(ans)越大,那么存在正环的概率也就越小,当(ans)越小时,(ans)大的情况肯定也会包含在里面,这就保证了单调性,可以使用二分答案来做

    至于以上的最长路,应该可以使用Dijkstra判断正环,但是我还是喜欢用SPFA,因为可以判负环,而且大部分时候快一些(什么鬼才逻辑)

    这里就直接上代码吧:

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=1e6+51;
    int n;
    string s[MAXN];
    struct node {
    	int net,to,w;
    } e[MAXN];
    int head[MAXN],tot;
    void add(int x,int y,double z) {
    	e[++tot].net=head[x];
    	e[tot].to=y;
    	e[tot].w=z;
    	head[x]=tot;
    } 
    int len[MAXN];
    bool v[MAXN];
    double d[MAXN];
    bool dfs(int x,double k) {
    	v[x]=true;
    	for(register int i=head[x]; i; i=e[i].net) {
    		int y=e[i].to;
    		double z=e[i].w;
    		if(d[y]<d[x]+z-k) {
    			d[y]=d[x]+z-k; //不能理解这个式子的参考上面的解题思路 
    			if(v[y]==true||dfs(y,k)==true) return true; 
    			//如果当前点走过说明有正环 
    		}
    	}
    	return v[x]=false; //记得回溯 
    }
    int main() {
    	while(scanf("%d",&n)) {
    		if(n==0) return 0;  //结束程序 
    		memset(head,0,sizeof head);
    		tot=0; //记得清空 
    		double l=0.0,r=1e8; //二分答案的范围,可以写大一点 
    		for(register int i=1; i<=n; i++) {
    			cin>>s[i];
    			len[i]=s[i].length();
    		} //输入,记录长度,每次调用函数会慢一点 
    		for(register int i=1; i<=n; i++) {
    			for(register int j=1; j<=n; j++) {
    				if(s[i][len[i]-1]==s[j][1]&&s[i][len[i]-2]==s[j][0]) {
    					add(i,j,len[i]); //枚举每两个字符串,如果可以相连就建边,边权就为字符串长度 
    				}
    			}
    		}
    		bool ok=false; //记录是否有答案 
    		while(r-l>0.001) { //实数域的二分,我的太丑啦~~ 
    			double mid=(l+r)/2;
    			bool op=0; //是否更新l和r 
    			memset(v,false,sizeof v);
    			memset(d,0,sizeof d); //清空数组 
    			for(register int i=1; i<=n; i++) {
    				if(dfs(i,mid)==true) { //如果有正环,说明有答案 
    					ok=true;
    					op=1;
    					l=mid;
    					break;
    				}
    			}
    			if(!op)r=mid; //搜不出来说明r太大了 
    		}
    		if(ok==false) puts("No solution");
    		else printf("%.2lf
    ",l*1.0);
    	}
    	return 0;
    }
    
  • 相关阅读:
    NYOJ题目22 素数求和
    最大连续子序列&&MAX SUM
    Computer Transformation
    #转 二分查找
    吃巧克力
    公司年会
    亲和串
    开门人和关门人
    找新朋友
    big number
  • 原文地址:https://www.cnblogs.com/Poetic-Rain/p/13368559.html
Copyright © 2020-2023  润新知