• poj 2288 Islands and Bridges (状压dp+Tsp问题)


    这道题千辛万苦啊!

    这道题要涉及到当前点和前面两个点,那就设dp[state][i][j]为当前状态为state,当前点为i,前一个点为j

    这个状态表示和之前做炮兵那题很像,就是涉及到三个点时,就多设一维表示前一个点(炮兵那题把点换成行)

    这道题有很多细节需要注意

    (1)计算路径长度。这道题一开始怎么不重复又方便的计算长度难住了我。

    后来看到题解直接在初始化的时候算上路径,非常牛逼

    然后前两个点的路径就包含进去了。

    首先加上前一个点和当前点的价值

    然后看有没有构成三角形,有就再加上

    (2)更新答案。这里更新答案要dp完了之后再弄,在dp时更新会出错

    多打几行代码不会死的,重要是要ac,麻烦一点就麻烦一点

    (3)初始化问题。这里谈谈填表法和刷表法初始化的不同

    如果是刷表法,那么就不知道当前状态合不合理,那就每次都需要判断一下

    一般来说一开始全部初始化为-1表示全部不合理,然后就把一开始合理的部分(比如起点)赋初值(一般为0)。

    如果是填表法的话,一般来说不需要判断合不合理

    但是这道题不一样,并不知道前两个点的状态是否合法,所以需要判断。

    (4)这道题有个比较坑的地方,就是n=1时要特判

    (5)然后自己头脑一定要清楚哪一个变量是第几个点!!

    我一般是写i是当前点,j是前一个点,k是前前个点

    (6)下标范围是0到n-1,那么就写1 << n

    (7)方案数。这道题方案数最后要除以2,因为可以反着走,但题目里算同一种

    然后dp弄方案一般可以开一个数组,意义是和dp数组一模一样的,只不过存的是方案数

    然后符合就加上

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define REP(i, a, b) for(int i = (a); i < (b); i++) 
    #define _for(i, a, b) for(int i = (a); i <= (b); i++) 
    using namespace std;
    
    typedef long long ll;
    const int MAXN = 15;
    int dp[(1 << 13) + 10][MAXN][MAXN], w[MAXN];
    int g[MAXN][MAXN], n, m; 
    ll ways[(1 << 13) + 10][MAXN][MAXN];
    
    int main()
    {
    	int T;
    	scanf("%d", &T);
    	
    	while(T--)
    	{
    		memset(g, 0, sizeof(g));
    		memset(dp, -1, sizeof(dp)); //初始化要注意 
    		memset(ways, 0, sizeof(ways));
    		
    		scanf("%d%d", &n, &m);
    		REP(i, 0, n) scanf("%d", &w[i]);
    		while(m--)
    		{
    			int u, v;
    			scanf("%d%d", &u, &v); u--; v--;
    			g[u][v] = g[v][u] = 1;
    		}
    		
    		if(n == 1) { printf("%d 1
    ", w[0]); continue; } //特判 
    		
    		REP(i, 0, n) //初始化 
    			REP(j, 0, n)
    				if(g[i][j])
    				{
    					dp[(1<<i)|(1<<j)][i][j] = w[i] + w[j] + w[i] * w[j];
    					ways[(1<<i)|(1<<j)][i][j] = 1;
    				}
    					
    		REP(S, 0, 1 << n)
    		  REP(i, 0, n) if(S & (1 << i))
    			REP(j, 0, n) if((S & (1 << j)) && g[i][j])
    			  REP(k, 0, n) if((S & (1 << k)) && g[j][k])
    			  {
    			  	if(i == j || j == k || i == k || dp[S^(1<<i)][j][k] == -1) continue; 
    			  	ll t = dp[S^(1<<i)][j][k] + w[i] + w[j] * w[i]; // 注意这里哪一个是最后一点 
    			  	if(g[i][k]) t += w[i] * w[j] * w[k];
    				   
    				if(dp[S][i][j] < t)
    				{
    					dp[S][i][j] = t;
    					ways[S][i][j] = ways[S^(1<<i)][j][k];
    				}
    				else if(dp[S][i][j] == t) ways[S][i][j] += ways[S^(1<<i)][j][k]; //这里是else if 写if会错 
    			  }
    		
    		ll ans = 0, num = 0; //分开来做 
    		int S = (1 << n) - 1;
    		REP(i, 0, n)
    			REP(j, 0, n)
    				if(g[i][j])
    				{
    					if(ans < dp[S][i][j])
    					{
    						ans = dp[S][i][j];
    						num = ways[S][i][j];
    					}
    					else if(ans == dp[S][i][j])
    						num += ways[S][i][j];
    				}
    		printf("%lld %lld
    ", ans, num / 2);
    	}
    		
    	return 0;
    }
  • 相关阅读:
    多选择文件打开对话框
    DirectoryExists
    获取IP地址
    获取WINDOWS特殊文件夹
    WPF WebBrowser
    DELPHI TDownLoadURL下载网络文件
    同步窗体移动 FormMove
    FireMonkey 使用Webbrowser
    网页截取图片
    FormMove
  • 原文地址:https://www.cnblogs.com/sugewud/p/9819326.html
Copyright © 2020-2023  润新知