HDU 5330 Route Statistics 【三进制状压DP】

题目来源:http://acm.hdu.edu.cn/showproblem.php?pid=5330
在这里插入图片描述
★如果n小很多,真的可以暴力awa 然而n大了 暴力还是出不了奇迹
这题目前只有两篇题解,而且感觉只有代码,看了好久才看懂,于是我就写了这篇详细一丢丢的题解


题意:

有n个点(暂且称为点吧),每个点都有一个012串(三进制,且长度都为m),每两个点的距离就是(这两个三进制串的每位相减的绝对值之和)。最后要求输出 距离和为 0到2*m 的点(a,b)的对数
另外(a,b)与(b,a)等价 不能重复,且(a,a)不考虑


思路:

暴力就不说了,不可能 我已经试过水了
我们可以构造一个 dp[ i ][ j ][ k ] 表示 与状态 j 距离为 k 的个数 ,那个 i 就是更新次数 最少m次 ★注意这里为了 节约内存 ,用了一点小技巧。因为每次更新之和他上一次有关,而最后只要求 最终结果,所以上上次及更上次都没必要保存,我们只需要 这次和上次 的结果 now 和 pre(这应该是叫 滚动数组?)
至于状压当然是 三进制状压,第二维开多大就看 3的11次方多大咯~
对于 答案为什么要除2 的问题,题意里面说清楚了,就是我们算了(a,b)和(b,a),所以要除2。那有人可能会问,我可以在 循环里面先除2 答案直接输出 吗?答案是 不行,如果ans[j]+=1ll*num[i]*dp[now][i][j]/2;这里的后面是 3*5/2 那岂不是损失了部分答案 算少了?所以不行。

★我自己也有个小问题没解决,如果不用 1ll ,把所有的数组开long long ,为什么会T,好奇怪。。。。


代码:

#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<deque>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=2e5+5;
const int sz=1<<16;
const int inf=2e9;
const int mod=1e9+7;
const double pi=acos(-1);
typedef long long LL;
int n,m;
int p[12],num[maxn];         //p数组是3进制要用的     num是记录出现次数
int dp[2][maxn][25];        // 详见上文    
LL ans[25];                //最终答案的二倍
template<class T>
inline void read(T &x)
{
    char c;x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}
int main()
{
    p[0]=1;
    for(int i=1;i<=11;i++) p[i]=p[i-1]*3;
//    cout<<p[11]<<endl;
    int t; read(t);
    while(t--){
        read(n); read(m);
        memset(num,0,sizeof num);
        for(int i=1;i<=n;i++){
            char c[20];
            scanf("%s",c);
            int sum=0;
            for(int j=1;j<=m;j++){
                sum=sum*3+c[j-1]-'0';
            }
            num[sum]++;
        }
        int now=0,pre=1;
        memset(dp[now],0,sizeof dp[now]);
        for(int i=0;i<p[m];i++) dp[now][i][0]=num[i];
        for(int i=0;i<m;i++){
            swap(now,pre);
            memset(dp[now],0,sizeof dp[now]);
            for(int j=0;j<p[m];j++){
                for(int k=0;k<=2*m;k++){
                    if(dp[pre][j][k]){
                        int tmp=j/p[i]%3;
                        for(int l=0;l<=2;l++){
                            dp[now][j-(tmp-l)*p[i]][k+abs(tmp-l)]+=dp[pre][j][k];
                        }
                    }
                }
            }
        }
        memset(ans,0,sizeof ans);
        for(int i=0;i<p[m];i++){
            if(num[i]){
                ans[0]+=1ll*num[i]*(num[i]-1);
                for(int j=1;j<=2*m;j++){
                    ans[j]+=1ll*num[i]*dp[now][i][j];
                }
            }
        }
        for(int i=0;i<=2*m;i++) cout<<ans[i]/2<<endl;
    }
    return 0;
}

发布了71 篇原创文章 · 获赞 89 · 访问量 8560

猜你喜欢

转载自blog.csdn.net/weixin_43890662/article/details/99298201
今日推荐