ABC338 F
观察到
错误示范:
考虑对于状态
#include<bits/stdc++.h> typedef long long LL; typedef std::pair<int,int> pii; #define fi first #define se second #define ok putstrln("OrzFinderHT") //#define check_time printf("%.8f\n",clocks()/CLOCK_PER_SEC) template<typename T> void abs(T &N){ if(N>=0) N=N; else N=-N; } namespace G_{ template<typename T> inline void read(T &a){ a=0; int f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') a=a*10+(int)(c-'0'),c=getchar(); a*=f; } inline void get_enter(){ getchar(); } inline void get_str(std::string &str){ char c=getchar(); while(c!=' '&&c!='\n') str+=c,c=getchar(); } template<typename T> inline void putN(T N){ char stk[70]; short top=0; if(N<0) putchar('-'),abs(N); do{ stk[++top]=(char)(N%10+(int)'0'); N/=10; }while(N); while(top) putchar(stk[top--]); } template<typename T> inline void putsp(T N){ putN(N); putchar(' '); } template<typename T> inline void putln(T N){ putN(N); putchar('\n'); } inline void putstr(std::string str){ int sz=str.size()-1; for(int i=0;i<=sz;i++) putchar(str[i]); } inline void putstrln(std::string str){ putstr(str); putchar('\n'); } inline void Yes(){ putstrln("Yes"); } inline void No(){ putstrln("No"); } template<typename T,typename I> inline void chkmin(T &a,I b){ a=std::min(a,b); } } //using namespace get_give; using namespace G_; const LL maxn=25,maxs=1<<20,inf=1e9; std::vector<pii>d[maxn]; LL dp[maxn][maxs]; signed main(){ int n,m; read(n),read(m); for(int i=1;i<=m;i++) { int u,v,w; read(u),read(v),read(w); d[u].push_back({v,w}); } int S=(1<<n)-1; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][(1<<(i-1))]=0; for(int s=1;s<S;s++) for(int i=1;i<=n;i++){ if(!((1<<(i-1))&s)) continue; for(auto q:d[i]){ // if((1<<(q.fi-1))&s) continue; int g=s|(1<<(q.fi-1)); chkmin(dp[q.fi][g],dp[i][s]+q.se); // printf("##%d %d %d %d !!%d\n",q.fi,g,i,s,dp[q.fi][g]); } } LL ans=inf; for(int i=1;i<=n;i++) chkmin(ans,dp[i][S]);//,printf("&&&%d %d\n",i,ans); if(ans==inf){ printf("No"); return 0; } printf("%lld\n",ans); // putN(ans); return 0; } /* -读入字符一定检查回车 - 能不能搜索? -函数要有返回值! -想好了再写! */
由于存在值为负的路径,所以使得我们重复经过一些边可以让答案更优。比如对于 11010 ,我们的转移一定来自 11000 或 10010 或 01010 。但是注意,当我们在 11010 这个状态到达 #2 时,若连接 #2 和 #4 的边是负的,那么我们可以通过这条边,使得 11010 状态下代价变小。
那如果我们允许从一个访问过的点到达另一个访问过的点呢?
还是对于 11010 ,我们会按照
我们又想到,可以用队列记录可能更新其它状态的状态,类似 dij 那样,但是如果用队列处理,会导致时间复杂度过大。
但是发现,只会有三个点 TLE ,且时间只比时限多出 0.6s(link),运用一些随机化技巧或许可以通过,可以自行尝试。
#include<bits/stdc++.h> typedef long long LL; typedef std::pair<int,int> pii; #define fi first #define se second #define ok putstrln("OrzFinderHT") //#define check_time printf("%.8f\n",clocks()/CLOCK_PER_SEC) template<typename T> void abs(T &N){ if(N>=0) N=N; else N=-N; } namespace G_{ template<typename T> inline void read(T &a){ a=0; int f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') a=a*10+(int)(c-'0'),c=getchar(); a*=f; } inline void get_enter(){ getchar(); } inline void get_str(std::string &str){ char c=getchar(); while(c!=' '&&c!='\n') str+=c,c=getchar(); } template<typename T> inline void putN(T N){ char stk[70]; short top=0; if(N<0) putchar('-'),abs(N); do{ stk[++top]=(char)(N%10+(int)'0'); N/=10; }while(N); while(top) putchar(stk[top--]); } template<typename T> inline void putsp(T N){ putN(N); putchar(' '); } template<typename T> inline void putln(T N){ putN(N); putchar('\n'); } inline void putstr(std::string str){ int sz=str.size()-1; for(int i=0;i<=sz;i++) putchar(str[i]); } inline void putstrln(std::string str){ putstr(str); putchar('\n'); } inline void Yes(){ putstrln("Yes"); } inline void No(){ putstrln("No"); } template<typename T,typename I> inline void chkmin(T &a,I b){ a=std::min(a,b); } } //using namespace get_give; using namespace G_; const LL maxn=25,maxs=1<<20,inf=1e9; std::vector<pii>d[maxn]; LL dp[maxn][maxs]; std::queue<pii>q; signed main(){ int n,m; read(n),read(m); for(int i=1;i<=m;i++) { int u,v,w; read(u),read(v),read(w); d[u].push_back({v,w}); } int S=(1<<n)-1; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][(1<<(i-1))]=0,q.push({i,1<<(i-1)}); while(q.size()){ int s=q.front().se,i=q.front().fi; q.pop(); for(int flag=1;flag;flag--){ if(!((1<<(i-1))&s)) continue; for(auto qq:d[i]){ // if((1<<(q.fi-1))&s) continue; int g=s|(1<<(qq.fi-1)); if(dp[qq.fi][g]>dp[i][s]+qq.se) q.push({qq.fi,g}); chkmin(dp[qq.fi][g],dp[i][s]+qq.se); // printf("##%d %d %d %d !!%d\n",q.fi,g,i,s,dp[q.fi][g]); } } } LL ans=inf; for(int i=1;i<=n;i++) chkmin(ans,dp[i][S]);//,printf("&&&%d %d\n",i,ans); if(ans==inf){ printf("No"); return 0; } printf("%lld\n",ans); // putN(ans); return 0; } /* -读入字符一定检查回车 - 能不能搜索? -函数要有返回值! -想好了再写! */
参见:link
正确示范:
考虑用 floyd 先处理出来两点之间的最短路,然后不枚举连边,直接对所有点更新,可以避免上面那种情况。
但是,我们注意到如果从
显然是不会的,设当前状态为
官方题解中先给出了一段看似显然的证明,或许意在利用 dp 的最优子结构性质来进行 dp ,但说明不够充分,大部分还需要感性理解。(显然,我的题解更适合作为官方题解)。
#include<bits/stdc++.h> typedef long long LL; typedef std::pair<int,int> pii; #define fi first #define se second #define ok putstrln("OrzFinderHT") //#define check_time printf("%.8f\n",clocks()/CLOCK_PER_SEC) template<typename T> void abs(T &N){ if(N>=0) N=N; else N=-N; } namespace G_{ template<typename T> inline void read(T &a){ a=0; int f=1; char c=getchar(); while(c<'0'||c>'9'){ if(c=='-') f=-1; c=getchar(); } while(c>='0'&&c<='9') a=a*10+(int)(c-'0'),c=getchar(); a*=f; } inline void get_enter(){ getchar(); } inline void get_str(std::string &str){ char c=getchar(); while(c!=' '&&c!='\n') str+=c,c=getchar(); } template<typename T> inline void putN(T N){ char stk[70]; short top=0; if(N<0) putchar('-'),abs(N); do{ stk[++top]=(char)(N%10+(int)'0'); N/=10; }while(N); while(top) putchar(stk[top--]); } template<typename T> inline void putsp(T N){ putN(N); putchar(' '); } template<typename T> inline void putln(T N){ putN(N); putchar('\n'); } inline void putstr(std::string str){ int sz=str.size()-1; for(int i=0;i<=sz;i++) putchar(str[i]); } inline void putstrln(std::string str){ putstr(str); putchar('\n'); } inline void Yes(){ putstrln("Yes"); } inline void No(){ putstrln("No"); } template<typename T,typename I> inline void chkmin(T &a,I b){ a=std::min(a,b); } } //using namespace get_give; using namespace G_; const LL maxn=25,maxs=1<<20,inf=1e9; std::vector<pii>d[maxn]; LL dp[maxn][maxs]; int dis[maxn][maxn]; signed main(){ int n,m; read(n),read(m); memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=m;i++) { int u,v,w; read(u),read(v),read(w); dis[u][v]=w; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) chkmin(dis[i][j],dis[i][k]+dis[k][j]); int S=(1<<n)-1; memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][(1<<(i-1))]=0; for(int s=1;s<S;s++) for(int i=1;i<=n;i++){ if(!((1<<(i-1))&s)) continue; for(int j=1;j<=n;j++){ // if((1<<(q.fi-1))&s) continue; int g=s|(1<<(j-1)); chkmin(dp[j][g],dp[i][s]+dis[i][j]); // printf("##%d %d %d %d !!%d\n",q.fi,g,i,s,dp[q.fi][g]); } } LL ans=inf; for(int i=1;i<=n;i++) chkmin(ans,dp[i][S]);//,printf("&&&%d %d\n",i,ans); if(ans==inf){ printf("No"); return 0; } printf("%lld\n",ans); // putN(ans); return 0; } /* -读入字符一定检查回车 - 能不能搜索? -函数要有返回值! -想好了再写! */
时间复杂度
参见此贴 。
贴中 rui_er 指出状压 dp 基础复杂度就是
本题的最优复杂度为
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具