zoukankan      html  css  js  c++  java
  • 洛谷 P1278 单词游戏 【状压dp】

    题目描述

    Io和Ao在玩一个单词游戏。

    他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致。

    游戏可以从任何一个单词开始。

    任何单词禁止说两遍,游戏中只能使用给定词典中含有的单词。

    游戏的复杂度定义为游戏中所使用的单词长度总和。

    编写程序,求出使用一本给定的词典来玩这个游戏所能达到的游戏最大可能复杂度。

    输入输出格式

    输入格式:

    输入文件的第一行,表示一个自然数N(1≤N≤16),N表示一本字典中包含的单词数量以下的每一行包含字典中的一个单词,每一个单词是由字母A、E、I、O和U组成的一个字符串,每个单词的长度将小于等于100,所有的单词是不一样的。

    输出格式:

    输出文件仅有一行,表示该游戏的最大可能复杂度。

    输入输出样例

    输入样例#1: 复制
    5
    IOO
    IUUO
    AI
    OIOOI
    AOOI
    输出样例#1: 复制
    16


    题解

    仔细观察就可以看出这是一张有向图,求最大无环路径权值

    听说这是一个NP完全问题,看数据范围那么小想都没想就暴搜了,信心满满地T了,结果70分,想想卡了个时【就是搜索一定次数直接输出答案】竟然A了。。

    但这不是正解。。。
    正解:

    状压dp
    第一次接触状压dp是NOIP2016愤怒的小鸟QAQ
    原来数据小的暴搜可以这样dp

    同样的道理,我们设f[s][i]表示s状态下走到了i的最大权值【s是二进制数,每一位代表对应的点是否走过】
    我们从0开始枚举s,对于每个点u,若s中包含了这个点u,那就可以看做走了那么多路径走到了u点,那么现在就可以从u点出发更新其他的点了
    对于u出发的到达的点v,如果没有被访问过,就可以进行一次状态转移


    f[s | (1 << v - 1)][v] = max(f[s | (1 << v - 1)][v],f[s][u] + d[u][v]);
    由于新的状态的值一定比当前状态要大,所以这样做没有后效性,是可行的算法


    暴搜卡时代码:
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define fo(i,x,y) for (int i = (x); i <= (y); i++)
    #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
    using namespace std;
    const int maxn = 20,maxm = 105,INF = 1000000000;
    
    inline int read(){
    	int out = 0,flag = 1;char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
    	return out * flag;
    }
    
    char s[maxn][maxm];
    int V[maxn],N,ans = 0;
    bool vis[maxn];
    
    int head[maxn],nedge = 0;
    struct EDGE{
    	int to,next;
    }edge[maxn * maxn];
    
    inline void build(int a,int b){
    	edge[nedge] = (EDGE) {b,head[a]};
    	head[a] = nedge++;
    }
    
    void init(){
    	fill(head,head + maxn,-1);
    	N = read();
    	REP(i,N){
    		scanf("%s",s[i]);
    		V[i] = strlen(s[i]);
    	}
    	REP(i,N) REP(j,N) if (i != j && s[i][V[i] - 1] == s[j][0]) build(i,j);
    }
    
    int cnt = 0;
    void dfs(int u,int sum){
    	cnt++;
    	if (cnt > 10000000){
    		cout<<ans<<endl;
    		exit(0);
    	}
    	vis[u] = true;
    	sum += V[u];
    	if (sum > ans) ans = sum;
    	Redge(u) if (!vis[edge[k].to]) dfs(edge[k].to,sum);
    	vis[u] = false;
    }
    
    int main()
    {
    	init();
    	REP(i,N) dfs(i,0);
    	cout<<ans<<endl;
    	return 0;
    }
    


    状压dp
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define fo(i,x,y) for (int i = (x); i <= (y); i++)
    #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next)
    using namespace std;
    const int maxn = 20,maxm = 1 << 16,INF = 1000000000;
    
    inline int read(){
    	int out = 0,flag = 1;char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}
    	return out * flag;
    }
    
    char s[maxn][105];
    int d[maxn],f[maxm][maxn],N,ans = 0;
    
    void init(){
    	N = read();
    	REP(i,N){
    		scanf("%s",s[i]);
    		d[i] = strlen(s[i]) - 1;
    	}
    }
    
    void solve(){
    	int maxv = (1 << N) - 1;
    	for (int i = 1; i <= N; i++)
    		f[1 << i - 1][i] = d[i] + 1;
    	for (int i = 0; i <= maxv; i++){
    		for (int j = 1; j <= N; j++){
    			int e1 = 1 << j - 1;
    			if ((i | e1) == i){
    				ans = max(ans,f[i][j]);
    				for (int k = 1; k <= N; k++){
    					int e2 = 1 << k - 1;
    					if (j != k && (i | e2) != i && s[j][d[j]] == s[k][0]){
    						f[i | e2][k] = max(f[i | e2][k],f[i][j] + d[k] + 1);
    					}
    				}
    			}
    		}
    	}
    	cout<<ans<<endl;
    }
    
    int main()
    {
    	init();
    	solve();
    	return 0;
    }
    

  • 相关阅读:
    Bootstrap-table一个表格组件神器(学习二)实战
    JS基础_for循环练习3
    JS基础_for循环练习2
    JS基础_for循环练习1
    JS基础_for循环
    JS基础_while的练习2
    JS基础_while的练习1
    JS基础_while循环
    JS基础_条件分支语句:switch语句
    JS基础_if注意问题
  • 原文地址:https://www.cnblogs.com/Mychael/p/8282851.html
Copyright © 2011-2022 走看看